Статьи

Обновленное руководство по навигации по гиперссылкам HTML в NetBeans

Введение в гиперссылки

Это будет работать в NetBeans 6.0, 6.1 с JDK 5, 6 . Так что имейте это в виду. Гиперссылки в IDE создаются при реализации класса HyperlinkProvider API NetBeans . В этом случае мы будем реализовывать многочисленные варианты использования . Прежде чем мы углубимся в это, давайте посмотрим на возможные гиперссылки в файле HTML.


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

 

Тип 1. Якоря, присутствующие в файлах HTML, например <a name=»anchorName»> </a>.

<a href=» #anchorName «> Вставьте текст между этими тегами, получите гиперссылку! </a>

 

Тип 2 — Якоря, подобные приведенным выше, присутствуют в ссылочных файлах HTML

<a href=» ref.html#anchorName «> Поместите текст между этими тегами, получите гиперссылку! </a>

Этот ref.html является ссылочным HTML-файлом, имеющим привязку типа 1 . Нажав на нее, вы перейдете к anchorName в файле ref.html .

 

Тип 3 — HTML-файлы со ссылками в том же каталоге

<a href=» ref.html «> Поместите текст между этими тегами, получите гиперссылку! </a>

Этот ref.html является ссылочным HTML-файлом, который находится в том же каталоге, что и файл с такими гиперссылками.

 

Тип 4 — HTML-файлы со ссылками в другом каталоге

<a href=» path/to/ ref.html «> Вставьте текст между этими тегами, получите гиперссылку! </a>

Этот ref.html является ссылочным HTML-файлом, который находится в каталоге с относительным путем ( путь / к / ).

 

Тип 5 — внешние URL

<a href=» http://platform.netbeans.org/tutorials/60/nbm-hyperlink.html «> Учебное руководство по навигации по гиперссылкам NetBeans </a>

HREF атрибут содержит внешний URL — адрес , который должен быть открыт через внешний браузер.

Теперь,

Гиперссылки появятся, когда пользователь удерживает нажатой клавишу Ctrl и наводит указатель мыши на значение атрибута HREF, как показано здесь:

На самом деле это гиперссылка типа 2 .

При нажатии на гиперссылку открывается указанный файл, и курсор попадает на первый якорь «Перейти», если он существует. Вот как будет выглядеть завершенный проект в окне «Проекты» (в котором представлен логический вид) — 

Создание проекта модуля

В этом разделе мы используем мастера для создания проекта модуля. Мы объявляем зависимости от модулей, которые предоставляют классы API NetBeans, необходимые для нашего модуля гиперссылки.

  1. Выберите « Файл»> «Новый проект» . В мастере создания нового проекта выберите « Модули NetBeans» в разделе «Категории» и « Проект модуля» в разделе «Проекты». Нажмите Далее . Введите AHrefHyperlink в поле « Имя проекта» и задайте «Расположение проекта» в соответствующей папке на вашем диске. Если они не выбраны, выберите «Автономный модуль» и выберите «Основной проект». Нажмите Далее .  
  2. Введите org.netbeans.modules.ahrefhyperlink в базе кодовых имен. Введите org / netbeans / modules / ahrefhyperlink / layer.xml   в слой XML. Нажмите Готово.

 


Вы должны добавлять зависимости при разработке, а не до этого.
Это позволит вам вспомнить, какие API необходимы для каких целей.

Реализуйте класс HyperlinkProvider

Класс HyperlinkProvider реализует три метода, каждый из которых подробно рассматривается ниже, сопровождаемый практическим примером в контексте нашего модуля. Сначала мы устанавливаем класс, а затем реализуем каждый из трех методов по очереди. Вы можете увидеть оригинальное руководство для всего этого раздела. Я просто перечислю изменения, сделанные в одной части кода. Рассмотрим следующий фрагмент в методе isHyperlinkPoint () :

                          case ARGUMENT:
if (AHREF_IDENTIFIER.equals(prev.text().toString())) {
identifier = tok.text().toString();
setLastDocument(doc);
startOffset = tokOffset;
endOffset = startOffset + tok.text().length();
return true;
}

Изменения, внесенные в это, в основном немного изменен

                            case ARGUMENT:
if (AHREF_IDENTIFIER.equals(prev.text().toString())) {
startOffset = tokOffset;
endOffset = startOffset + tok.text().length();
++startOffset;
--endOffset;
return true;
}

Почему я сделал
++ startOffset и
—endOffset ?

Причина в том, что учебник показывал
«ref.html» как гиперссылку, и двойные кавычки были удалены в
OpenHTMLThread с введением переменной cleanedIdentifier! Принимая во внимание, что я хотел видеть только промежуточный текст как гиперссылку, когда я наводил курсор мыши, пока
нажата клавиша
CTRL , т.е.
ref.html как гиперссылка.

Что случилось с
setLastDocument (doc) ?

Удалите этот установщик для
переменной
lastDocument, использованной в
исходном уроке , а также удалите эту переменную, я не мог ее использовать, поэтому удалил ее.
performClick ()

Рассмотрим и этот метод,
    //Start a new thread for opening the HTML document:
OpenHTMLThread run = new OpenHTMLThread(styledDocdoc, identifier);
RequestProcessor.getDefault().post(run);

Я заменил это следующим кодом
        identifier = doc.getText(0, doc.getLength()).substring(startOffset, endOffset);

if (identifier.contains("://")) {
try {
URLDisplayer.getDefault().showURL(new URL(identifier));
} catch (MalformedURLException ex) {
Exceptions.printStackTrace(ex);
}
} else {
//Start a new thread for opening the HTML document-
OpenHTMLThread run = new OpenHTMLThread(doc, identifier);
RequestProcessor.getDefault().post(run);
}


Обратите внимание, что при замене вы увидите красную линию под 1-м оператором этого фрагмента, нажмите
Alt + Enter. Всплывающие подсказки редактора, чтобы окружить try-catch или выбросить исключение. Итак, выберите предыдущую, и ваш executeClick () теперь выглядит следующим образом:

Пожалуйста, сделайте небольшое исправление, передайте doc в конструктор OpenHTMLThread , когда его экземпляр создается в этом методе.

Поиск гиперссылок

Далее вам нужно создать класс, который открывает файл HTML в отдельном потоке. Здесь класс называется OpenHTMLThread .

Маркер, указанный в методе isHyperlinkPoint (), получен этим классом. Затем токен анализируется, чтобы увидеть, содержит ли он косую черту, которая указывает на то, что это относительная ссылка. В этом случае объект файла извлекается из URL-адреса файла. В противном случае объект файла создается из самого токена. Далее мы увидим, как реализовать различные типы гиперссылок, упомянутые во введении.

Refer-Type 1, 2

Нажатие на #anchorName приведет вас к anchorName в текущем документе или в файле ref.html .

Refer-Type 3, 4

Открывается документ с именем файлового объекта и курсор помещается на тег BODY , если он найден. В противном случае пользователь получит уведомление в строке состояния в среде IDE NetBeans «Файл не найден!».

Refer-Type 5

HREF атрибут содержит внешний URL — адрес , который должен быть открыт через внешний браузер с помощью следующей code-

URLDisplayer.getDefault().showURL(new URL(identifier));


Фоновый процесс — OpenHTMLThread

Маркер, указанный в методе isHyperlinkPoint (), получен этим классом. Затем токен анализируется, чтобы увидеть, содержит ли он косую черту, которая указывает на то, что это относительная ссылка. В этом случае объект файла извлекается из URL-адреса файла. В противном случае объект файла создается из самого токена. Все варианты использования, упомянутые в предыдущем разделе, реализованы с использованием locateAnchorName () , setPosition ()

public class OpenHTMLThread implements Runnable {

//<editor-fold desc="Variables & Constructor">
private StyledDocument doc;
private String identifier;
private String[] args;

public OpenHTMLThread(Document doc, String identifier) {
super();
this.doc = (StyledDocument) doc;
this.identifier = identifier;
}
boolean anonFlag = true;

public void setAnonFlag(boolean anonFlag) {
this.anonFlag = anonFlag;
}
//</editor-fold>
public void run() {

args = identifier.split("/");
String splitIdentifier = args[args.length - 1];
if (splitIdentifier.contains("#")) {
final String anchorName = splitIdentifier.substring(splitIdentifier.indexOf("#") + 1);
if (splitIdentifier.charAt(0) == '#') {
locateAnchorName(args, anchorName, true);
} else {
locateAnchorName(args, anchorName, false);
}
} else {
verifyHyperlinkStatus(splitIdentifier);
}
}
findHTML ()
Находит
FileObject упомянутого HTML-файла, если он имеет относительный путь, также заботится о наличии привязки в
идентификаторе . Также реализован случай, когда такие файлы не могут быть найдены
    private FileObject findHTML() {
FileObject fo = NbEditorUtilities.getFileObject(doc);
FileObject foHtml = null;

//<editor-fold desc="Finding file...">
// Here we're working out whether we're dealing with a relative link or not:
if (identifier.contains("/")) {
String fullPath = fo.getPath();
try {
URL f = new File(fullPath).toURI().resolve(identifier.split("#")[0]).toURL();
foHtml =
URLMapper.findFileObject(f);
} catch (MalformedURLException ex) {
Logger.getLogger(OpenHTMLThread.class.getName()).log(Level.SEVERE, null, ex);
}

} else {
foHtml = fo.getParent().getFileObject(identifier.split("#")[0]);
}
//</editor-fold>
if (foHtml == null) {
StatusDisplayer.getDefault().setStatusText("File can't be found...");
}

return foHtml;
}
locateAnchorName (конечная String [] арг, конечная строка AnchorName, логический флаг)

Этот метод позволяет IDE находит
линию , которая состоит из декларации якоря, как упомянуто в usecases-

 
    private void locateAnchorName(final String[] args, final String anchorName, boolean flag) {
if (flag) {
final JTextComponent editor = EditorRegistry.lastFocusedComponent();
org.netbeans.editor.Utilities.runInEventDispatchThread(new Runnable() {

public void run() {
setPosition(editor.getDocument(), anchorName);
}
});
} else {
verifyHyperlinkStatus(args[args.length - 1]);
}
}
 
openEditor (DataObject dObject, final String anchorName)

Это позволяет вам открыть редактор, в основном
findHTML () и
openEditor () ранее были частью метода
run () , однако я разделил его на различные повторно используемые методы.

    private void openEditor(DataObject dObject, final String anchorName) {
final EditorCookie.Observable ec = (EditorCookie.Observable) dObject.getCookie(EditorCookie.Observable.class);

if (ec != null) {
org.netbeans.editor.Utilities.runInEventDispatchThread(new Runnable() {

public void run() {
final JEditorPane[] panes = ec.getOpenedPanes();

//<editor-fold desc="Positioning the Cursor...">
if ((panes != null) && (panes.length > 0)) {
setPosition(panes, 0, anchorName);
} else {
ec.addPropertyChangeListener(new PropertyChangeListener() {

public void propertyChange(PropertyChangeEvent evt) {
if (EditorCookie.Observable.PROP_OPENED_PANES.equals(evt.getPropertyName())) {
final JEditorPane[] panes = ec.getOpenedPanes();
if ((panes != null) && (panes.length > 0)) {
setPosition(panes, 0, anchorName);
}
ec.removePropertyChangeListener(this);
}
}
});
ec.open();
}
//</editor-fold>
}
});
}
}
verifyHyperlinkStatus (String splitIdentifier)
Это в основном проверяет, присутствует ли идентификатор в гиперссылке, если файл существует по относительному пути, то открыт ли он ИЛИ нет. Если нет, то открывает его, также заботится о наличии якорей.
    private void verifyHyperlinkStatus(String splitIdentifier) {
try {
//<editor-fold desc="Verfying Status...">
final TopComponent[] tc = TopComponent.getRegistry().getOpened().toArray(new TopComponent[0]);
for (int k = 0; k < tc.length; k++) {
final EditorCookie editor = tc[k].getLookup().lookup(EditorCookie.class);
if (editor != null) {
DataObject d = tc[k].getLookup().lookup(DataObject.class);
if (d != null) {
final FileObject fo = d.getPrimaryFile();
//final int index = k;
final String[] hyperLink = splitIdentifier.split("#");
if (hyperLink[0].equals(fo.getNameExt())) {
EventQueue.invokeAndWait(new Runnable() {

public void run() {
StatusDisplayer.getDefault().setStatusText("Regaining Focus...");
JEditorPane[] panes = editor.getOpenedPanes();
if (hyperLink.length > 1) {
setPosition(panes, 0, hyperLink[1]);
} else {
setPosition(panes, 0, null);
}
//tc[index].requestActive();
setAnonFlag(false);
}
});
if (anonFlag == false) {
break;
}
}
}
}
}
//</editor-fold>
if (anonFlag) {
try {
DataObject dObject;
dObject = DataObject.find(findHTML());

if (splitIdentifier.contains("#")) {
openEditor(dObject, splitIdentifier.split("#")[1]);
} else {
openEditor(dObject, null);
}
} catch (DataObjectNotFoundException ex) {
Logger.getLogger(OpenHTMLThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
} catch (InterruptedException ex) {
Logger.getLogger(OpenHTMLThread.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(OpenHTMLThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

Перегруженные методы-

После того, как два метода перегружены, они используются для установки позиции каретки на определенной строке или в некоторой конкретной точке.

setPosition (Document d, String identifier)

Использует
NbEditorUtilities, чтобы показать точную строку упомянутого
anchorName или
тега <body> !

    private void setPosition(Document d, String identifier) {
try {
//Gets the content of the document-
String text = d.getText(0, d.getLength() - 1);

//Selecting the apt position...if exists, cursor is placed-
int index = text.indexOf("<body>");
if (identifier != null) {
if ((index = text.indexOf("name")) > 0) {
int loc = text.indexOf(identifier, index);
index = loc + identifier.length();
if (text.charAt(index) == '\"') {
NbEditorUtilities.getLine(d, loc,
true).show(Line.SHOW_GOTO);
return;
}
}
if ((index = text.indexOf("\"#" + identifier + "\"")) > 0) {
StatusDisplayer.getDefault().setStatusText("Anchor doesn't exist...");
NbEditorUtilities.getLine(d, index,
true).show(Line.SHOW_GOTO);
return;
}
}
if (index > 0) {
NbEditorUtilities.getLine(d, index,
true).show(Line.SHOW_GOTO);
}

} catch (BadLocationException ex) {
Logger.getLogger(OpenHTMLThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
SetPosition (JEditorPane [] панель, внутр поз, идентификатор String)
Позволяет использовать как
NbEditorUtilities , а также,
setCaretPosition () из
JEditorPane , чтобы показать точную линию / курсор упомянутых
AnchorName или
<тело> тег! 
 
    private void setPosition(JEditorPane[] pane, int pos, String identifier) {
try {
//Gets the content of the document-
Document d = pane[pos].getDocument();
String text = d.getText(0, d.getLength() - 1);

//Selecting the apt position...if exists, cursor is placed-
int index = text.indexOf("<body>");
if (identifier != null) {
if ((index = text.indexOf("name")) > 0) {
int loc = text.indexOf(identifier, index);
index = loc + identifier.length();
if (text.charAt(index) == '\"') {
NbEditorUtilities.getLine(d, loc,
true).show(Line.SHOW_GOTO);
// pane[pos].setCaretPosition(loc);
return;
}
}
if ((index = text.indexOf("\"#" + identifier + "\"")) > 0) {
StatusDisplayer.getDefault().setStatusText("Anchor doesn't exist...");
NbEditorUtilities.getLine(d, index,
true).show(Line.SHOW_GOTO);
// pane[pos].setCaretPosition(index);
return;
}
}
if (index > 0) {
NbEditorUtilities.getLine(d, index,
true).show(Line.SHOW_GOTO);
// pane[pos].setCaretPosition(index);
}

} catch (BadLocationException ex) {
Logger.getLogger(OpenHTMLThread.class.getName()).log(Level.SEVERE, null, ex);
}
}

Убедитесь, что объявлены следующие операторы импорта: 
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.StyledDocument;
import org.netbeans.api.editor.EditorRegistry;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.openide.awt.StatusDisplayer;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.URLMapper;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.text.Line;
import org.openide.windows.TopComponent;

Регистрация класса реализации HyperlinkProvider

Наконец, вам нужно зарегистрировать класс реализации поставщика гиперссылок в файле layer.xml модуля . Сделайте это следующим образом, убедившись, что строка, выделенная жирным шрифтом ниже, является полностью определенным именем класса, который реализует HyperlinkProvider-

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<folder name="Editors">
<folder name="text">
<folder name="html">
<folder name="HyperlinkProviders">
<file name="AHrefHyperlinkProvider.instance">
<attr name="instanceClass"
stringvalue="org.netbeans.modules.ahrefhyperlink.AHrefHyperlinkProvider"/>
<attr name="instanceOf"
stringvalue="org.netbeans.lib.editor.hyperlink.spi.HyperlinkProvider"/>
</file>
</folder>
</folder>
</folder>
</folder>
</filesystem>

Если вы создаете гиперссылку для другого типа MIME, вам нужно изменить вышеуказанные папки text / html на соответствующий тип MIME.



Работа впереди

Итак, какие еще варианты использования могут быть реализованы.

Предположим, атрибут HREF содержит

Вызов функции JavaScript, определение которой присутствует

  • В том же документе, ИЛИ
  • Присутствует в ссылочном JavaScript.

Если вы создаете гиперссылку для другого типа MIME, вам нужно изменить вышеуказанные папки text / html на соответствующий тип MIME.


Рекомендации