Статьи

Как использовать защищенные разделы в редакторах на платформе NetBeans


Меня попросили реализовать нередактируемый раздел, такой как те, которые в файлах Java не позволяют редактировать код Матиссом.
Я не должен был позволять пользователю редактировать некоторые переменные, синтаксис которых был похож на $ {ABC}.

Я скопировал код, который используется для BeanInfo, особенно «BIEditorSupport.java». Ниже приведены шаги для создания нового типа файла и добавления к нему защищенных разделов. Конечно, его можно настроить по собственному желанию.

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

Спасибо Маттео Ди Джовинаццо и Паоло Репеле за их обзор кода.

Примечание . Полный пример кода можно скачать здесь .

  1. Создайте новое «приложение платформы NetBeans». Назовите его «GuardedSectionSuite», подойдет любое другое имя. Для этого в меню «Файл» выберите «Новый проект …» / «Модули NetBeans» / «Приложение платформы NetBeans». Завершите работу мастера, затем щелкните правой кнопкой мыши узел пакета, выберите «Свойства», затем выберите «Библиотеки». В кластере «ide» проверьте узел «Editor Guarded Sections».
  2. Добавьте новый модуль. Разверните узел набора. Щелкните правой кнопкой мыши узел «Модули». Выберите «Добавить новый …». Вызовите модуль «GuardedSectionModule», например. Добавьте новый модуль в комплект. Нажмите «Далее». Убедитесь, что установлен флажок «Создать слой XML». Нажмите «Готово». Щелкните правой кнопкой мыши на новом узле модуля и выберите «Свойства», а затем «Библиотеки». Нажмите «Добавить зависимость», а затем добавьте зависимости «Общие запросы API», «Разделы, защищенные редактором», «UI Utilities API», если вы не добавите это, у вас возникнут проблемы при запуске пакета.
  3. Распознать новый тип файла. Щелкните правой кнопкой мыши на новом узле модуля и выберите «Новый» / «Другой». Выберите «Разработка модуля» и «Тип файла». Заполните текстовое поле, расширение — это расширение, используемое для распознавания файла, а тип MIME будет идентификатором этого файла. Нажмите «Далее». Опять заполните поля, значок не важен. Нажмите «Готово».
  4. Напиши код. Изменить «ScantiDataObject.java» Заменить эту строку:
    cookies.add((Node.Cookie)DataEditorSupport.create(this, getPrimaryEntry(), cookies));

    с этим

    cookies.add(new ScantiDesignEditorSupport(this, getCookieSet()));

    Создайте новый класс Java и назовите его «ScantiDesignEditorSupport». Этот класс позаботится о сохранении и загрузке файла и косвенно вызовет «GuardedSectionProvider» в этих случаях. Не забудьте изменить его, если у вас есть тип MIME, отличный от «text / x-scanti». Скопируйте этот код внутри «ScantiDesignEditorSupport»:

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.Reader;
    import java.io.Writer;
    import java.nio.charset.Charset;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.EditorKit;
    import javax.swing.text.StyledDocument;
    import org.netbeans.api.editor.guards.GuardedSectionManager;
    import org.netbeans.api.queries.FileEncodingQuery;
    import org.netbeans.spi.editor.guards.GuardedEditorSupport;
    import org.netbeans.spi.editor.guards.GuardedSectionsFactory;
    import org.netbeans.spi.editor.guards.GuardedSectionsProvider;
    import org.openide.cookies.EditCookie;
    import org.openide.cookies.EditorCookie;
    import org.openide.cookies.OpenCookie;
    import org.openide.cookies.PrintCookie;
    import org.openide.cookies.SaveCookie;
    import org.openide.filesystems.FileLock;
    import org.openide.filesystems.FileObject;
    import org.openide.loaders.DataObject;
    import org.openide.loaders.MultiDataObject;
    import org.openide.nodes.CookieSet;
    import org.openide.text.DataEditorSupport;
    import org.openide.windows.CloneableOpenSupport;

    public class ScantiDesignEditorSupport extends DataEditorSupport
    implements OpenCookie, EditCookie, EditorCookie, PrintCookie, EditorCookie.Observable {

    private MyGuardedEditor guardedEditor;

    public ScantiDesignEditorSupport(DataObject obj, CookieSet cookieSet) {
    super(obj, new Environment(obj, cookieSet));
    setMIMEType("text/x-scanti");
    }

    /**
    * gets the GuardedSectionManager from a given document
    * Useful to get a list of Guarded Sections inside a document
    * @param doc the document
    * @return a GuardedSectionManager
    */
    public GuardedSectionManager getGuardedSectionManager(StyledDocument doc) {
    return GuardedSectionManager.getInstance(doc);
    }

    public GuardedSectionManager getGuardedSectionManager() {
    try {
    StyledDocument doc = openDocument();
    return GuardedSectionManager.getInstance(doc);
    } catch (IOException ex) {
    throw new IllegalStateException("cannot open document", ex); // NOI18N
    }
    }

    @Override
    protected void loadFromStreamToKit(StyledDocument doc, InputStream stream, EditorKit kit)
    throws IOException, BadLocationException {
    guardedEditor = null;

    GuardedSectionsProvider guardedProvider = getGuardedProvider(doc);

    if (guardedProvider != null) {
    guardedEditor.document = doc;
    Charset c = FileEncodingQuery.getEncoding(this.getDataObject().getPrimaryFile());
    Reader reader = guardedProvider.createGuardedReader(stream, c);
    try {
    kit.read(reader, doc, 0);
    } finally {
    reader.close();
    }
    } else {
    super.loadFromStreamToKit(doc, stream, kit);
    }
    }

    private GuardedSectionsProvider getGuardedProvider(StyledDocument doc) {
    GuardedSectionsProvider guardedProvider = null;
    if (guardedEditor == null) {
    guardedEditor = new MyGuardedEditor(doc);
    String mimeType = ((DataEditorSupport.Env) env).getMimeType();
    GuardedSectionsFactory gFactory = GuardedSectionsFactory.find(mimeType);
    if (gFactory != null) {
    guardedProvider = gFactory.create(guardedEditor);
    }
    }
    return guardedProvider;
    }

    @Override
    protected void saveFromKitToStream(StyledDocument doc, EditorKit kit, OutputStream stream)
    throws IOException, BadLocationException {
    GuardedSectionsProvider guardedProvider = getGuardedProvider(doc);
    if (guardedProvider != null) {
    Charset c = FileEncodingQuery.getEncoding(this.getDataObject().getPrimaryFile());
    Writer writer = guardedProvider.createGuardedWriter(stream, c);
    try {
    kit.write(writer, doc, 0, doc.getLength());
    } finally {
    writer.close();
    }
    } else {
    super.saveFromKitToStream(doc, kit, stream);
    }
    }

    @Override
    public void saveDocument() throws IOException {
    super.saveDocument();
    }

    @Override
    protected boolean notifyModified() {
    if (!super.notifyModified()) {
    return false;
    }
    ((Environment) this.env).addSaveCookie();
    return true;
    }

    @Override
    protected void notifyUnmodified() {
    super.notifyUnmodified();
    ((Environment) this.env).removeSaveCookie();
    }

    @Override
    protected void notifyClosed() {
    super.notifyClosed();
    }

    static class MyGuardedEditor implements GuardedEditorSupport {

    protected StyledDocument document;

    public MyGuardedEditor(StyledDocument document) {
    this.document = document;
    }

    @Override
    public StyledDocument getDocument() {
    return document;
    }
    }

    static ScantiDesignEditorSupport findEditor(DataObject dobj) {
    return dobj.getLookup().lookup(ScantiDesignEditorSupport.class);
    }

    private static final class Environment extends DataEditorSupport.Env {

    private static final long serialVersionUID = -1;
    private final transient CookieSet cookieSet;
    private transient SaveSupport saveCookie = null;

    private final class SaveSupport implements SaveCookie {

    @Override
    public void save() throws java.io.IOException {
    DataObject dobj = getDataObject();
    ((DataEditorSupport) findCloneableOpenSupport()).saveDocument();
    dobj.setModified(false);
    }
    }

    public Environment(DataObject obj, CookieSet cookieSet) {
    super(obj);
    this.cookieSet = cookieSet;
    }

    @Override
    protected FileObject getFile() {
    return this.getDataObject().getPrimaryFile();
    }

    @Override
    protected FileLock takeLock() throws java.io.IOException {
    return ((MultiDataObject) this.getDataObject()).getPrimaryEntry().takeLock();
    }

    public
    @Override
    CloneableOpenSupport findCloneableOpenSupport() {
    return findEditor(this.getDataObject());
    }

    public void addSaveCookie() {
    DataObject javaData = this.getDataObject();
    if (javaData.getCookie(SaveCookie.class) == null) {
    if (this.saveCookie == null) {
    this.saveCookie = new SaveSupport();
    }
    this.cookieSet.add(this.saveCookie);
    javaData.setModified(true);
    }
    }

    public void removeSaveCookie() {
    DataObject javaData = this.getDataObject();
    if (javaData.getCookie(SaveCookie.class) != null) {
    this.cookieSet.remove(this.saveCookie);
    javaData.setModified(false);
    }
    }
    }
    }
  5. Создайте новый класс Java. Назовите это «ScantiGuardedSectionsFactory». Вставьте этот код внутри:
    import org.netbeans.spi.editor.guards.GuardedEditorSupport;
    import org.netbeans.spi.editor.guards.GuardedSectionsFactory;
    import org.netbeans.spi.editor.guards.GuardedSectionsProvider;

    public class ScantiGuardedSectionsFactory extends GuardedSectionsFactory{

    @Override
    public GuardedSectionsProvider create(GuardedEditorSupport ges) {
    return new ScantiGuardedSectionsProvider(ges);
    }

    }
  6. Наконец, создайте класс Java, который выполняет эту работу. Назовите это «ScantiGuardedSectionsProvider». Метод readSection считывает содержимое файла и извлекает из него разделы. Возвращаемый объект Result содержит массив символов и список разделов. Я думаю, что отображаемый текст можно изменить, вернув другой массив. То же самое можно сказать и о методе writeSections. Это полезно, если вы хотите пометить свой раздел текстом, который вы не хотите показывать пользователю.
    import java.util.ArrayList;
    import java.util.List;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.swing.text.BadLocationException;
    import org.netbeans.api.editor.guards.GuardedSection;
    import org.netbeans.api.editor.guards.SimpleSection;
    import org.netbeans.spi.editor.guards.GuardedEditorSupport;
    import org.netbeans.spi.editor.guards.support.AbstractGuardedSectionsProvider;
    import org.openide.util.Exceptions;


    public class ScantiGuardedSectionsProvider extends AbstractGuardedSectionsProvider {

    public ScantiGuardedSectionsProvider(GuardedEditorSupport editor) {
    super(editor);
    }

    @Override
    public char[] writeSections(List list, char[] chars) {
    return chars;
    }

    @Override
    public Result readSections(char[] chars) {
    List sections = new ArrayList();

    StringBuilder sb = new StringBuilder();

    int start = 0;
    int len = 0;
    for (int i = 0; i < chars.length; i++) {
    char c = chars[i];

    switch (c) {
    case '}': // I have found the closing brace
    if (len == 2) {
    try {
    Logger.getLogger(ScantiGuardedSectionsProvider.class.getName()).log(Level.INFO, "Appending Section {0}", sb.toString());
    SimpleSection section = createSimpleSection(sb.toString(), start, i);
    sections.add(section);
    } catch (BadLocationException ex) {
    Exceptions.printStackTrace(ex);
    }
    sb = new StringBuilder();

    len = 0;
    }
    break;
    case '$': // I have found the first character
    if (len == 0) {
    len++;
    }
    start = i;
    break;

    case '{': // secondcharacter
    if (len == 1) {
    len++;
    }
    break;

    default: // I create the variable name
    if (len == 2) {
    sb.append(c);
    }
    break;


    } // end of switch
    }

    Result res = new Result(chars, sections);

    return res;
    }

    }
  7. Отредактируйте «layer.xml», чтобы зарегистрировать фабрику. Добавьте эти теги:
    <folder name="Editors">
        <folder name="text">
            <folder name="x-scanti">
                <file name="org-mycompany-guardedsection-ScantiGuardedSections.instance">
                    <attr name="instanceCreate" newvalue="org.mycompany.guardedsection.ScantiGuardedSectionsFactory"/>
                    <attr name="instanceOf" stringvalue="org.netbeans.spi.editor.guards.GuardedSectionsFactory"/>
                </file>
            </folder>
        </folder>
    </folder>
  8. Запустите модуль. Создайте новый файл и напишите в нем текст, убедившись, что в нем есть что-то вроде $ {ABC}. Сохраните файл с расширением «.scanti», закройте его и снова откройте, и защищенные разделы будут применены.