Для роботи зі статтею потрібно встановити NetBeans 4.0 і пакет SDK Java (також званий JDK).
Спочатку розглянемо нову середу IDE і один з вбудованих прикладів додатків. У першому розділі, широко використовуються знімки екрана; з цим розділом можна працювати відразу після установки NetBeans. Потім розглянемо використання NetBeans для редагування, компіляції та виконання нескладного програми Java. У міру просування кількість знімків екрана буде зменшуватися, а обсяг вихідного коду - збільшуватися. Темп роботи (розширення прикладу додатки "гра в анаграми") буде прискорюватися в міру вивчення розширених можливостей NetBeans по підвищенню продуктивності праці. До кінця статті читач познайомиться з використанням NetBeans для написання коду Java для відкриття і синтаксичного аналізу документів XML, запиту користувача введення за допомогою простих діалогових вікон і багато чого іншого. І, зрозуміло, буде зроблено величезний крок до самостійного створення та розширення додатків Java. Поїхали!
- Перші кроки: ознайомлення з NetBeans і початок проекту
- Зміна вихідного коду
- Занадто передбачувано?
- Важка порція java.util.Random
- Завантаження списків слів з файлів
- Список пов'язаних файлів
При першому запуску NetBeans з меню "Пуск" завантаження завершилася приблизно за 15 секунд. У деяких випадках перша завантаження може зайняти більше часу: NetBeans знайомиться з системою.
Після завантаження можна озирнутися - в разі сумнівів тисніть "Скасування"! Для продовження натисніть кнопку "Приклад проекту" під вітальним зображенням.
За замовчуванням як місце знаходження проекту вибирається домашня папка. Створіть папку "code" на диску C :, а в ній - папку "java". Потім виберіть цю створену папку в якості місця розташування проекту за допомогою кнопки "Огляд", як показано на наступному скріншоті.
Після натискання кнопки "Готово" в діалоговому вікні вище відкривається діалогове вікно "Сканування шляху до класів", яке залишається відкритим приблизно протягом хвилини.
Тепер на вкладці "Проекти" на лівій "панелі" відображається єдиний вузол з заголовком "AnagramGame". Привітальна заставка залишається відкритою в "головної" області. Розгорніть вузол "AnagramGame" на вкладці "Проекти" і послідовно розгорніть всі дочірні вузли, поки не стане видно наступна деревоподібна структура:
На малюнку вище розмір головного вікна NetBeans змінений, і видно тільки ліва "панель".
Зауважимо, що цей приклад програми складається з двох "вихідних пакетів" (один з них - бібліотека, а другий - інтерфейс користувача) та одного "тестового пакета" (для бібліотеки). Також необхідно відзначити, що кожен клас крім полів / конструкторів / методів містить вузол "Шаблони компонентів" (для всіх поточних класів, показаних вище, однак, цей вузол порожній). У цій статті вузли "Шаблон компонентів" не використовуються, однак вони дуже корисні для підвищення модульності, коду, його гнучкості і придатності до повторного використання за допомогою компонентів JavaBeans .
Двічі клацніть вузол "About.java"; відкриється наступна вкладка:
Вкладку вітання в головній області / панелі можна закрити. Знову надамо читачеві можливість вільно досліджувати NetBeans: можна відкривати файли з відкритим вихідним кодом, переглядати візуальні "форми" для деяких класів інтерфейсу користувача або переглядати файли, які містять вихідного коду, шляхом перемикання з вкладки "Проекти" на вкладку "Файли" на лівій панелі . На цій панелі можна переглянути різні файли .properties і скрипти побудови Ant. Вікно "Параметри", що викликається з меню "Сервіс", дозволяє оцінити гнучкість і різноманітність можливостей настройки середовища IDE!
Тепер зупинимося і ще раз відрепетируємо закриття і відкриття NetBeans. Цього разу серед IDE відкриється з порожньою головною областю, а в області "Проекти" виявиться проект "AnagramGame". Тепер натисніть кнопку "Виконати головний проект (F6)" на панелі інструментів (вона жовто-зелена; для перегляду заголовка кнопки наведіть на неї курсор). У середовищі IDE NetBeans відкриється вікно "Висновок" з наступним текстом:
init: deps-jar: Created dir: C: \ code \ java \ AnagramGame \ build \ classes Compiling 3 source files to C: \ code \ java \ AnagramGame \ build \ classes compile: run:
Крім того, відкриється невелике вікно - призначений для користувача інтерфейс програми AnagramGame.
Я відразу відгадав! Можливо, це пов'язано з тим, що серед каталогів і файлів, переглянутих раніше, виявився довгий список слів (ймовірно, розшифрованих слів), які могли дещо спростити завдання. Запам'ятаємо це на майбутнє ...
Спочатку просто змінимо текст одного з повідомлень, що виводяться користувачеві у прикладі програми. На панелі "Проекти" двічі клацніть вузол com.toy.anagrams.ui.Anagrams. Його вихідний код відкриється в новій вкладці в головній області вікна. У верхньому лівому кутку вкладки розташована кнопка "Конструктор" і "Вихідний кол". Натисніть кнопку "Вихідний файл", якщо вона ще не натиснута - відкриється вихідний код Java для класу Anagrams в редакторі. Клацніть правою кнопкою миші ліве поле і переконайтеся в тому, що встановлений прапорець "Показати номери рядків". Перейдіть до рядка №211 (натисніть сполучення клавіш CTRL + G). Рядок повинен виглядати наступним чином:
feedbackLabel.setText ( "Incorrect! Try again!");
Змінимо текст (тривіально), скомпілюємо його, виконаємо додаток, неправильно відгадає слово і розглянемо змінене повідомлення. На малюнку показаний приклад нового повідомлення:
feedbackLabel.setText ( "Not quite ... please try again!");
На цей раз зверніть увагу, що на вкладці редактора між словом "Anagrams" і "X" (команда закриття) з'явилася маленька зірочка. Ця зірочка означає, що файл змінено, але не збережено. Збережіть зміни: Файл-> Зберегти (або CTRL + S). Отже на даний момент вихідний файл змінити і зберегти, проте в скомпільованому файлі класу все ще старий код. NetBeans дозволяє виконати дві дії разом: клацніть зелений / жовтий значок "Виконати головний проект" або просто натисніть клавішу F6. Знову відкриється додаток. В консолі виведення, проте відображається наступний текст:
init: deps-jar: Compiling 1 source file to C: \ code \ java \ AnagramGame \ build \ classes compile: run:
Як видно, перед виконанням програми NetBeans (точніше скрипт побудови Ant) виявив, що файл змінено, і скомпілював його перед виконанням "завдання виконання". Спробуйте відгадати слово, бажано неправильно. Якщо все пройшло правильно, з'явиться повідомлення з текстом, налаштованим вище.
Зіграйте пару раз; автор, наприклад, не зміг осилити "iccrmutsnaec". Для демонстрації наступних найважливіших можливостей NetBeans смухлюем і підгляне рішення в вихідному коді. Припустимо, що перетасовані версії слів зберігаються в одному з вихідних файлів проекту, а не генеруються кожен раз випадковим чином з вихідного слова. Незабаром ми переконаємося, що саме так і йдуть справи в поточній реалізації; трохи пізніше ми вживемо заходів з цього приводу ...
На вкладці "Проекти" клацніть правою кнопкою миші вузол "Пакети з вихідними файлами" і виберіть пункт контекстного меню "Пошук ...". Відкриється наступний екран з заголовком "Пошук":
На цьому знімку екрана видно введений критерій пошуку "iccrmutsnaec". Залиште інші параметри налаштування без зміни і натисніть кнопку "Пошук" для запуску пошуку. Поруч з консоллю виведення відкриється нова вкладка з результатами пошуку. Як показано на малюнку, виявлено один збіг - у файлі "WordLibrary.java", на рядку №53:
Двічі клацніть збіг (вузол, відзначений як "iccrmutsnaec", [позиція 63:10] вище). Відповідний вихідний файл відкриється в редакторі, відповідний рядок буде переведена в поле зору, і буде підсвічена збігається фраза. Виявляється, "iccrmutsnaec" - шоста запис в масиві рядків; цей масив називається "SCRAMBLED_WORD_LIST". Зверніть увагу, що прямо над цим масивом розташований інший масив, проте записи в ньому - нормальні англійські слова. Цей масив називається "WORD_LIST". Ймовірно, до цього моменту всі вже здогадалися, що рішення для слова "iccrmutsnaec" - це шоста запис в масиві "WORD_LIST". Зрозуміло, творець програми міг застосувати і менш очевидну схему відображення між розшифрованими і зашифрованими словами, проте на щастя він цього не зробив! Тому на рядку №16 можна спостерігати слово "circumstance". Введіть це слово в додаток (якщо воно вже закрилося, повторіть гру до необхідного моменту); відкриється наступний екран:
Тепер ми знаємо, як відкрити файл для редагування, змінити вихідний код, скомпілювати і виконати додаток, виконати пошук по вихідного коду і зшахраювати в грі в анаграми. Спробуємо об'єднати ці навички, щоб зробити додаток AnagramGame трохи цікавіше. Для цього реалізуємо динамічну генерацію перетасувати слів, позбувшись таким чином від необхідності вести список перетасувати слів у вихідному коді. В результаті зникне одна з можливостей для шахрайства. Потім забезпечимо випадковий характер генеруються перетасувати слів, щоб гра була цікавішою.
У вихідному коді WordLibrary видно метод "getScrambledWord". Розглянувши цей метод, можна помітити, що його реалізація надзвичайно проста:
return SCRAMBLED_WORD_LIST [idx];
В цьому місці ми і будемо вносити зміни. Почнемо з зміни цього рядка (№126) на наступну:
return generateScrambledWord (idx);
Цей рядок коду зазначається в середовищі IDE NetBeans підкресленням червоною хвилястою лінією, що вказує на синтаксичну помилку в вихідному коді. Перемістіть курсор в будь-яку частину підкресленого вихідного коду: виводиться лише незначна частина повідомлення про те, що сигнатура методу generateScrambledWord (int) не розпізнає. Зрозуміло, цей метод ще не створений. Створимо його. Метод просто додається в кінець класу, починаючи з рядка №147:
/ ** * Пошук відповідного слова в словнику для зазначеного індексу * і генерація перетасованої версії цього слова. * @Param idx індекс перетасовуваної слова * @return перетасована версія слова * / protected static String generateScrambledWord (int idx) {int j = 0; String word; String scrambled = ""; java.util.Random r; r = new java.util.Random (); word = getWord (idx); for (j = 0; j <word.length (); j ++) {if (r.nextBoolean ()) scrambled = scrambled + word.charAt (j); else scrambled = word.charAt (j) + scrambled; } Return scrambled; }
Завдання функції generateScrambledWord (int) полягає в поверненні перетасованої версії словникової записи, зазначеної цілочисельним аргументом - індексом. Безсумнівно, можна придумати безліч різних способів тасування (зміни порядку) букв в слові, і читач вільний реалізувати тіло цього методу будь-яким доречним способом. Можна, наприклад, просто звернути порядок букв, але тоді гра стане, ймовірно, занадто простий (проте, спробуйте!). Також можна попарно переставити місцями сусідні букви від початку до кінця слова. Застосуйте уяву і знайдіть алгоритм, цікавий в реалізації і видає цікаві результати. Зупинимося на реалізації, наведеної в прикладі. Вбудоване засіб генерації випадкових чисел Java використовується тут таким чином, щоб результат кожної тасування певного слова був завжди однаковий. Перетасувати слово конструюється побуквенно, причому кожна наступна буква додається в початок або в кінець фрагмента слова в залежності від який випав випадкового числа. Таким чином формується нове слово, що складається з усіх букв вихідного слова, проте в новій і випадкової послідовності. Тепер виконайте отримане додаток. На цей раз автору не вдалося вгадати багато слів, проте через випадкового характеру складання послідовностей час від часу випадає перетасувати слово, дуже близьке до вихідної форми. Для усунення цього недоліку можна, наприклад, змінити алгоритм, наведений вище, і змінювати порядок пар символів, а не окремих символів, через які слова простіше відгадати. Якщо у читача вийде алгоритм, який робить гру особливо захоплюючою, просимо поділитися ним з усіма читачами статті.
Словник перетасувати слів (масив SCRAMBLED_WORD_LIST) більше не використовується, тому його можна сміливо видалити з вихідного файлу WordLibary.java (рядки з 57 по 104).
У новій версії гри вгадати слово набагато важче. Автор, наприклад, був абсолютно фрустрована такий складністю. Що ж робити? Відомо, що вихідні слова зберігаються в простому словнику, також відомо, що в ході гри слова беруться з цього словника по черзі, від початку до кінця. Тому в поточному коді, так як ми маємо доступ до вихідного файлу, ми все одно можемо схитрувати, відстеживши порядковий номер чергового слова і підглянувши його в словнику. При запуску гри випало слово "noiatabsrct", яке автору не вдалося відразу відгадати. Однак на рядку №11 в файлі WordLibrary.java видно, що перше слово - "abstraction". Наступне завдання - поставити випадковий порядок вибірки слів зі словника. Для цього знову скористаємося класом Random з пакета java.util. Оскільки цей клас використовується вже вдруге, звернемося до документації по його інтерфейсу API за базовими відомостями про його функціях.
Примірник цього класу використовується для генерації потоку псевдовипадкових чисел. Використовується 48-розрядний початкове число, яке змінюється по лінійної конгруентної формулою. (Докладні відомості наведені в книзі Дональда Кнута "Мистецтво програмування", том 2, розділ 3.2.1). Якщо створити два примірника класу Random з однаковими початковими числами і викликати їх методи в однаковій послідовності, будуть отримані ідентичні послідовності числових значень.
Поки що заради зручності використовувався конструктор java.util.Random без аргументів. Коментарі до інтерфейсу API, наведені вище, вказують на необхідність ініціалізації кожного примірника Random з унікальним початковим числом; в іншому випадку при повторному використанні програми послідовність може повторитися. з документації по конструктору без аргументів видно, що фактично в ньому в якості початкового числа використовується поточний час (в мілісекундах), так що існуючий код придатний до використання, і можна перейти до більш суттєвих питань.
Які зміни потрібні для встановлення випадкового порядку вибірки слів зі словника? У класі WordLibrary є два методи, що відносяться до справи: як getWord (int idx), так і getScrambledWord (int idx) приймають в якості параметра індекс слова в словнику. Додайте курсор на імені методу getWord, потім клацніть його та виберіть пункт меню "Пошук використань". Цей метод викликається тричі (один раз з WordLibraryTest і два рази власне з WordLibrary). Кожен з цих викликів використовується всередині компонента WordLibrary (в т.ч. в інфраструктурі тестування), і тут що-небудь міняти не потрібно. Далі, переведіть курсор на ім'я методу getScrambledWord і натисніть клавіші ALT + F7. Знову виводяться три випадки використання методу, проте два з них - з класу Anagrams. Розглянемо їх докладніше. В обох випадках (рядки 28 і 195) значення параметра, переданого в getScrambledWord - змінна wordIdx. Сама змінна оголошується на рядку 22 і инициализируется нульовим значенням. Виконаємо пошук випадків використання змінної worldIdx: їх три.
- На рядку 28 конструктор гри виконує початкове заповнення елемента управління перетасувати слів першим словом словника.
- У методі nextTrialActionPerformed змінна wordIdx збільшується на одиницю (зверніть увагу, що вона скидається до нуля після перебору всіх слів), а потім елемент управління перетасувати слів заповнюється новим значенням відповідно до цієї змінної.
- У методі guessedWordActionPerformed виконується обробка спроби користувача, причому поточне значення змінної wordIdx використовується для пошуку відповідного слова.
Тепер ми бачимо, що змінна wordIdx визначає слово, вибирається після кожної спроби, і ця змінна змінюється тільки в одному місці, в рядку №192 файлу Anagrams.java:
wordIdx = (wordIdx + 1)% WordLibrary.getSize ();
Для встановлення випадкового порядку вибірки слів зі словника необхідно змінити цей рядок так, щоб значення wordIdx було випадковим і знаходилося між нижньою і верхньою межами словника. Цього дозволяє домогтися метод java.util.Random.nextInt (int n), тому замінимо рядок №192 наступним рядком коду:
wordIdx = new java.util.Random (). nextInt (WordLibrary.getSize ());
Перевіримо, чи досягнуто необхідний ефект, тобто зіграємо знову! Перше слово вже не викликає труднощів, це завжди перетасувати "abstraction". Справді, конструктор Anagram на початку гри завжди обирає перше слово зі словника, оскільки змінної wordIdx статично присвоюється нульове початкове значення. Однак після першого слова все наступні варіанти непередбачувані, що і було потрібно. Отже, гра стала трохи цікавіше, і залишається всього один шахрайський спосіб (зрозуміло, за наявності доступу до вихідного коду). При бажанні можна також реалізувати випадковий характер вибірки першого слова в грі. Для цього необхідно просто обчислити початкове значення змінної wordIdx тим же способом, що і наступні значення - рядок №22 файлу Anagrams.java тепер виглядає наступним чином:
private int wordIdx = wordIdx = new java.util.Random (). nextInt (WordLibrary.getSize ());
Ймовірно, у читача також не викличе труднощів слово "elxical" - перетасувати "lexical" - однак при вигляді "lasiugsidintinhbe" вже хочеться натиснути кнопку "Нове слово". Чому "indistinguishable" в середньому складніше, ніж "lexical"? Чим менше буїв у вихідному слові, тим більш імовірно (при поточному алгоритмі тасування), що перетасована форма буде мало відрізнятися від вихідної. У більш загальному вигляді, однак, короткі слова простіше розшифрувати, ніж довгі, незалежно від алгоритму. Для початківця гравця був би цікавішим набір коротких слів, а для досвідченого - довгих. Зрозуміло, крім довжини слова є й інші ознаки, наприклад, рідкість слова, частина мови, мову або тематика. Чи можуть ці спостереження зробити гру цікавіше, і чи можна в процесі дізнатися що-небудь нове про NetBeans і Java?
В цьому розділі словник виноситься з програми, тобто в грі можна використовувати кілька різних списків слів, і користувачі навіть можуть надавати власні списки слів. Для цього існує, зрозуміло, кілька способів; в даному випадку ця функціональна можливість реалізується шляхом читання файлу з структурованим списком слів в форматі XML. До кінця розділу читач познайомиться з відкриттям і читанням файлів з локальної файлової системи або з веб-файлів і з ефективним синтаксичним аналізом нескладного файлу XML.
Для завантаження зовнішнього списку слів доцільно додати новий пункт меню. Для цього перемкнемося в режим проектування. Виконаємо перехід від такого уявлення:
До такого:
У режимі проектування справа також відображається панель "Інспектор". Спочатку зовнішній вигляд панелі наступний:
На панелі "Інспектор" показана ієрархічна структура компонентів Swing, складових графічний інтерфейс користувача. Якщо розгорнути вузол [JFrame], видно, що перший компонент називається mainMenu [JMenuBar]. Він, в свою чергу, містить у собі елемент fileMenu [JMenu], в який входять ще два вузла: aboutMenuItem [JMenuItem] і exitMenuItem [JMenuItem]. Як видно, кожен вузол позначений ім'ям відповідної змінної; за нею йде тип компонента в квадратних дужках. Головний фрейм - це власне клас Anagrams (спадкоємець JFrame), тому в класі Inspector не показано ім'я примірника для цього першого компонента. Наше завдання полягає в додаванні команди меню для завантаження зовнішнього списку слів, тому скористаємося "інспектором" і новий елемент між позиціями "About" (Про програму) і "Exit" (Вихід) в меню "File" (Файл).
Клацніть правою кнопкою миші вузол fileMenuItem і виберіть "Додати", а потім "JMenuItem". Новий пункт меню додається в третій позиції після пункту "Exit". Спочатку в середовищі IDE NetBeans цього пункту присвоюється автоматично створене ім'я; змінимо його на "openNewListMenuItem". Для цього необхідно клацнути новий пункт правою кнопкою миші і вибрати "Перейменувати". Нарешті, знову клацніть правою кнопкою миші новий пункт і виберіть "Перемістити вгору": буде встановлений необхідний порядок.
Тепер виберіть пункт exitMenuItem і натисніть кнопку «Властивості" на панелі "Властивості" безпосередньо під "інспектором". Серед інших тут є три цікавих властивості: mnemonic, text, toolTipText. Для даного конкретного пункту меню значення цих властивостей відповідно "E", "Exit" і "Quit Team, Quit!". Виберіть новий пункт меню і змініть значення його властивостей на "O", "Open Word List", "Choose a new word list file, replacing the current list." відповідно.
Потім додамо код, який повинен виконуватися при виборі цього пункту меню користувачем. NetBeans знову поспішає на допомогу: клацніть правою кнопкою миші в "інспектора" вузол openNewListMenuItem і виберіть в меню, що розкриваються "Події", а потім "Действия", потім "actionPerformed". Після створення методу обробки подій буде виконаний перехід назад до вистави "Вихідний код" для написання необхідної реалізації. Новим методом автоматично присвоюється ім'я "openNewListMenuItemActionPerformed", проте його можна безпечно перейменувати за допомогою команди "Реорганізація коду". Невеликий відступ для читачів, які мають досвід роботи із засобами проектування графічних інтерфейсів користувача Swing: Яке ж життєстверджуюче видовище є чиста і ладна структура, імена та форматування автоматично згенерованого коду! Наприклад, код для трьох пунктів в меню "Файл" наводиться в тому ж порядку, що і самі пункти. Повернемося до справи. Коли користувач вибирає пункт меню "Open Word List" (Відкрити список слів), йому пропонується вибрати файл в локальній файловій системі, який потім відкривається, синтаксично аналізується та використовується як список слів. Якщо файл недоступний чи не містить дійсного списку слів, гра триває з поточним списком слів. Ось з чого ми починали:
private void openNewListMenuItemActionPerformed (java.awt.event.ActionEvent evt) {// TODO власний код обробки:}
Завантажувач приймає в якості головного параметра адреса URL, тому необхідно згенерувати адреса URL відповідно до імені файлу, обраним користувачем. Спочатку створимо екземпляр JFileChooser і відкриємо з його допомогою діалогове вікно "Відкрити файл". Якщо користувач вибере файл, сгенерируем відповідний адресу URL шляхом додавання протоколу file: /// до абсолютного шляху до файлу.
private void openNewListMenuItemActionPerformed (java.awt.event.ActionEvent evt) {/ * Змінна "initialDirectory" визначає півроку відображається каталог * при виборі переліку слів. У разі зазначення порожнього рядка огляд почнеться * з каталогу за замовчуванням для користувача. У Windows це зазвичай "Мої Документи", * а в * nix - домашній каталог користувача. * / String initialDirectory = ""; String fileURL; JFileChooser chooser; int returnVal; chooser = new JFileChooser (initialDirectory); returnVal = chooser.showOpenDialog ((Component) evt.getSource ()); if (returnVal == chooser.APPROVE_OPTION) {/ * Для створення адреси URL для файлу в локальній файловій системі просто * додамо в початок абсолютного шляху до файлу протокол "file". * / FileURL = "file: ///" + chooser.getSelectedFile (). GetAbsolutePath (); this.loadWordList (fileURL); }}
Показаний вище метод ілюструє надання користувачеві діалогове вікно вибору файлів; потім викликається поки непізнаний метод loadWordList. Цей метод приймає як параметр файл зі списком слів і, якщо файл в порядку, поточний список слів замінюється списком з файлу. Метод виглядає наступним чином:
/ ** * Спроба відкрити файл XML зі списком слів і провести його * синтаксичний аналіз. У разі успіху список слів використовується замість * поточного списку. * Адреса URL може вказувати, приміром, на документ в локальній файловій системі, наприклад * "file: /// C: /testlist.xml", або на документ у всесвітній мережі Інтернет, * наприклад "http: //www.stanford. edu / ~ bsuter / simplewordlist.xml ". * * @Param url Адреса документа XML зі списком слів. * / Protected void loadWordList (String url) {/ * Створення примірника завантажувача * / com.toy.anagrams.lib.WordListFileLoader wlfl = new com.toy.anagrams.lib.WordListFileLoader (); / * Відкриття та синтаксичний аналіз файлу, заміна поточного списку слів завантаженим списком. * / WordLibrary.setWordList (wlfl.loadList (url)); / * Оскільки кількість слів у новому списку може відрізнятися від попереднього, * необхідно оновити значення wordIdx відповідно до нового списком. * / WordIdx = new java.util.Random (). NextInt (WordLibrary.getSize ()); }
У гру вступає новий клас з ім'ям WordListFileLoader. Крім того, в класі WordLibrary з'явився новий метод setWordList. Метод доданий на рядку №125, він дуже простий:
public static void setWordList (String [] wordList) {if (wordList! = null) WORD_LIST = wordList; }
Повний вихідний код класу WordListFileLoader доступний тут , Проте зупинимося на найважливіших частинах. У цьому новому класі один істотний метод, а також внутрішній клас-спадкоємець org.xml.sax.helpers.DefaultHandler. Примірник WordListFileLoader призначений для відкриття, читання, синтаксичного аналізу та оцінки вмісту файлу XML з локальної файлової системи або з документа XML, доступного по HTTP. Розглянемо високорівневе опис роботи методу WordListFileLoader.loadList:
- отримання InputStream для зазначеного документа;
- створення синтаксичного аналізатора SAX (Simple API for XML) для цього документа;
- настройка обробника, що реагує на всі події, що надходять від синтаксичного аналізатора SAX.
Існує кілька способів синтаксичного аналізу документів XML засобами Java. Поширений підхід DOM (Document Object Model) пов'язаний з формуванням в пам'яті ієрархічної моделі безлічі вузлів документа XML. У нашому випадку структура набору даних проста (список слів), і проаналізований список негайно конвертується в масив String [], тому зберігання документа XML в пам'яті не має великого сенсу. Підхід SAX заснований на подіях: клас-обробник очікує події від синтаксичного аналізатора, які виникають, коли синтаксичний аналізатор стикається з елементом XML, з атрибутом або з кінцем елемента. Оброблювач може обробляти або ігнорувати такі події. Якщо умови завдання цьому сприяють, підхід SAX бажаний, оскільки з його допомогою можна обробляти дуже об'ємні документи.
public String [] loadList (String url) {InputStream is; try {URL u = new URL (url); is = u.openConnection (). getInputStream (); } Catch (IOException ioe) {report ( "Не вдалося відкрити або знайти вказаний список слів."); is = null; } If (is == null) {report ( "Не вдалося завантажити вказаний список слів. Помилка при відкритті файлу."); return null; } SAXParserFactory parserFactory = SAXParserFactory.newInstance (); SAXParser parser; try {parser = parserFactory.newSAXParser (); } Catch (ParserConfigurationException pce) {report ( "Помилка при налаштуванні синтаксичного аналізатора XML. Синтаксичний аналізатор не налаштований належним чином. Завантаження припинена."); return null; } Catch (SAXException saxe) {report ( "Помилка при налаштуванні синтаксичного аналізатора XML. Завантаження припинена."); return null; } Try {WordListHandler handler = new WordListHandler (); parser.parse (is, handler); } Catch (Exception e) {report ( "Не вдалося завантажити список, імовірна помилка синтаксичного аналізу SAX."); return null; } Return this.list; }
Як зазначено вище, обробник SAX повинен реалізувати різні обробники подій, в нашому випадку їх рівно три: початок елемента, вміст елемента та закриття елемента. Підсумковий код виглядає наступним чином:
public class WordListHandler extends DefaultHandler {protected String nodeType; protected ArrayList al; public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException {if (qName == "word") {nodeType = «word"; } Else if (qName == »wordlist") {al = new ArrayList (); }} Public void endElement (String uri, String localName, String qName) throws SAXException {if (qName == "word") {nodeType = null; } Else if (qName == "wordlist") {/ * ArrayList наводиться до масиву об'єктів String. * / List = (String []) this.al.toArray (new String [al.size ()]), }} Public void characters (char [] chars, int start, int length) throws SAXException {if (this.nodeType == "word") {this.al.add (new String (chars, start, length) .trim ( )); }}}
У структурі документа зі списком слів присутні елементи XML всього двох типів: <word> і <wordlist>. У наведеному коді видно, що фактичне додавання нового слова до списку виконується за допомогою методу characters. Решта методи відстежують опрацьований вузол і виконують дії з налаштування та очищення, такі як ініціалізація нового списку слів і оновлення поля списку зовнішнього класу.
Повний вихідний код Java для WordListFileLoader наведено тут . Приклад документа списку слів наведено тут ; він також може виглядати наступним чином:
<? Xml version = "1.0" encoding = "utf-8"?> <Wordlist> <word> welcome </ word> <word> jacuzzi </ word> <word> elefant </ word> <word> abracadabra </ word> </ wordlist>
Отже, тепер можна створювати власні списки слів шляхом створення і редагування файлів XML в текстовому редакторі. Згодом, безсумнівно, можна придумати безліч захоплюючих тематичних списків слів. Було б здорово поділитися ними з широкою публікою? Ще було б зовсім чудово користуватися списками слів, створеними іншими людьми - такі слова було б важче вгадати. Хороша новина: в нашій програмі це вже майже реалізовано. Погана новина: на цей раз автор утримається від покрокових вказівок. Спочатку необхідно створити новий пункт меню (наприклад "Download File List" - ціни загрузка) аналогічно створеній команді "Open File List". Додайте метод для пов'язаного дії actionPerformed (як в минулий раз) і використовуйте наступний код в якості реалізації методу:
private void downloadNewListMenuItemActionPerformed (java.awt.event.ActionEvent evt) {String listURL; String prompt = "Введіть адресу URL списку слів в форматі XML:"; String initialValue = "http: //"; listURL = (String) javax.swing.JOptionPane.showInputDialog (prompt, initialValue); / * Нескладна перевірка допустимості перед спробою завантаження нового списку слів * / if (listURL! = Null && listURL.length ()> 0 && listURL! = InitialValue) {this.loadWordList (listURL); }}
Більш докладних відомостей в цій статті наведено не буде, проте автор сподівається, що вдалося продемонструвати ефективність і прямолінійність створення чітких і правильних графічних інтерфейсів користувача за допомогою засобу проектування форм NetBeans. Зрозуміло, розкриті тільки найпростіші можливості однак до додатка AnagramGame удалося додати цінні функціональні можливості і тепер в нашому розпорядженні проста платформа, що дозволяє випробувати нові концепції і глибше розібратися в NetBeans. Найцікавіше, зрозуміло, реалізувати власні ідеї, однак ризикнемо запропонувати кілька для початку:
- Кожен список слів може бути забезпечений необов'язковою атрибутом "заголовок", який можна виводити в заголовку вікна програми.
- Якщо список слів не надто великий, одне і те ж слово може часто повторюватися, роблячи гру більш передбачуваною і менш цікавою. Щоб уникнути цього ефекту, можна реалізувати кеш недавно використаних слів. Для кожного наступного запиту слова можна було б перевіряти, чи присутній нове слово в кеші; якщо це так, можна запитати наступне слово, поки не буде отримано слово, якого немає у кеші. Таким чином можна знизити число повторень. Для зниження частоти повторень з точки зору користувача розмір кешу, ймовірно, може бути досить невеликим, проте його розмір завжди повинен бути менше, ніж кількість слів у списку, інакше можна отримати нескінченний цикл.
- Можна створити інший завантажувач списку слів, що приймає будь-який текстовий документ. Такий завантажувач проводив би синтаксичний аналіз всіх строкових маркерів і створював список слів, що містить всі унікальні слова. Можливо, варто встановити мінімальну довжину слова. Щоб зовсім ускладнити гру, направте цей завантажувач на іноземний новинний сайт!
Змінена додаток AnagramGame у вигляді виконуваного файлу JAR. Натисніть для гри! simplelist.xml Приклад списку слів у вигляді документа XML. WordListFileLoader.java Синтаксичний аналізатор списків слів у форматі XML, заснований на SAX. anagrams-src.zip Повний вихідний код Java зміненим додатком AnagramGame.
Що ж робити?Які зміни потрібні для встановлення випадкового порядку вибірки слів зі словника?
Чому "indistinguishable" в середньому складніше, ніж "lexical"?
Чи можуть ці спостереження зробити гру цікавіше, і чи можна в процесі дізнатися що-небудь нове про NetBeans і Java?
Encoding = "utf-8"?
Було б здорово поділитися ними з широкою публікою?