Введение в гиперссылки
Это будет работать в 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, необходимые для нашего модуля гиперссылки.
- Выберите « Файл»> «Новый проект» . В мастере создания нового проекта выберите « Модули NetBeans» в разделе «Категории» и « Проект модуля» в разделе «Проекты». Нажмите Далее . Введите AHrefHyperlink в поле « Имя проекта» и задайте «Расположение проекта» в соответствующей папке на вашем диске. Если они не выбраны, выберите «Автономный модуль» и выберите «Основной проект». Нажмите Далее .
- Введите 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.
Рекомендации
- Руководство по навигации по гиперссылкам NetBeans. Отдельное спасибо Geertjan за то, что он позволил мне просмотреть и пересмотреть руководство.
- DevFaq на редакторе
- Списки рассылки OpenIDE — Отдельное спасибо Wade и Vita !
- Реактивировать открытый редактор Windows