Занятие третье

На данном занятии мы попрактикуемся в создании аплета с помощью JBuilder. Наш аплет будет сканировать всю файловую систему того диска, с которого он запускается.

Воспользуемся мастером создания аплета, расположенным в диалоговой панели New, вызываемой командой File New New Applet.

На экране появится диалоговая панель, в которой задается основная, формирующая скелет проекта информация.

Для начала нужно заполнить поле Package, в которое будет помещен класс, а затем поле Class (название класса). Можно пренебречь именем пакета. В этом случае будет создана безымянная группа. Однако лучше не игнорировать поле Package и давать пакетам осмысленные имена, иначе спустя некоторое время вы получите форменную свалку из безымянных пакетов и файлов. И уж поверьте, вам придется основательно покопаться в исходных текстах, чтобы разобраться. Мы будем благоразумны и создадим отдельный пакет Applets. Вы можете размещать там все создаваемые аплеты. Класс нашего аплета будет называться скромно - Applet1. После ввода этой информации вы увидите, что в поле File появится полное имя генерируемого файла исходного текста аплета, включая полный путь к каталогу, в котором он будет размещен. Это полезно для контроля правильности действий.

Теперь при желании можно воспользоваться тремя отмечаемыми кнопками, включающими дополнительные полезные возможности. Выбрав кнопку Generate header comments, вы инициируете генерацию специального заголовка, располагающегося в самом начале исходного текста аплета. Заголовок содержит общую информацию о проекте, которая, по вашему мнению, будет интересна тем, кому придется читать ваши исходные тексты. Эта информация пригодится и вам, если спустя некоторое время вы решите снова обратиться к старому проекту.

Следующая кнопка, Can run standalone, будучи включенной, приводит к генерации статического метода main(), в котором происходит вызов класса аплета. Это весьма полезно при отладке класса Java без применения браузера. Кроме того, вы получаете класс, который может запускаться и как аплет, и как приложение. В последнем случае нужно найти в начале исходного текста аплета описание переменной:

boolean isStandalone = false;
и заменить его строкой:
boolean isStandalone = true;

Теперь вы можете запустить аплет как оконное приложение, используя Java-загрузчик с именем java.exe или его отладочную версию java_g.exe. Это приведет к появлению обычного окна класса Frame, внутри которого "вклеен" аплет. Не правда ли, удобный способ?

Последняя кнопка, Generate standard methods, определяет, следует ли мастеру New Applet произвести генерацию стандартных методов, присущих аплету: start(), stop() и destroy(). Два первых вызываются при запуске и остановке аплета соответственно. Метод destroy() будет вызван после того, как выполнение аплета остановлено, и объект аплета должен быть уничтожен. Как правило, эти методы программисты используют для захвата ресурсов и освобождения их по завершении работы. В нашем примере мы включаем кнопку Generate standard methods для создания каркаса метода start() - а зачем он нужен, станет ясно чуть позже. И хотя это мелочь, но из таких тактических мелочей складывается успешная стратегия программирования. Введя информацию, нажимаем Next>, чтобы перейти на следующий этап генерации аплета.

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

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

Начнем с того, что введем в поле Name имя планируемого параметра - showFiles. Если присвоить ему значение True, то наш аплет проведет детальное сканирование файловой системы. В свою очередь значение False приведет к сканированию только на уровне каталогов, без учета файлов. Далее определим тип создаваемого параметра. Очевидно, раз этот параметр может иметь лишь два состояния, для него нужно использовать логический тип boolean, который вы выбираете из раскрывающегося списка в поле Type. Следующее поле Desc служит для ввода комментариев к параметрам. В общем-то особой необходимости в его использовании нет, но для порядка напишем в нем Show all (показать все). Если аплет будет запускаться кем-нибудь другим, комментарии помогут определить, для чего нужен тот или иной параметр. В следующем поле - Variable - нужно указать название переменной, в которую будет помещаться значение считанного из HTML-страницы параметра. Исходный текст, производящий считывание параметров и запись их в указанные вами переменные, генерируется средой JBuilder автоматически. Чтобы имя переменной было и в дальнейшем понятным, можно к нему прибавить префикс var - varShowFiles. Данный способ создания имен является личным изобретением автора, которым он пользуется давно и находит его удобным. Вы же можете задавать имя и по-другому, поскольку здесь нет никаких правил. Главное, чтобы вам самим было понятно, для чего предназначена та или иная переменная. Закончив создание параметра, мы нажимаем на кнопку Next>. Если же вам нужен не один, а несколько параметров, то после ввода информации о каждом нажмите кнопку Add Parameter и введите данные для получения следующего параметра.

Последние действия мастера Applet производят создание HTML-страницы, обрамляющей наш аплет. Она позволяет протестировать работу аплета с помощью браузера WWW или утилиты AppletViewer (входит в поставку как пакета JBuilder, так и стандартного набора разработчика JDK).

Для начала нужно проверить, отмечена ли кнопка Generate HTML Page. Если выключить ее, HTML-страница создана не будет. Затем введите заголовок страницы, набрав его текст в поле Title. В следующем поле Name введите имя, которое будет присвоено аплету. Через него вы сможете ссылаться на аплет из скриптовых программ (написанных на языках управления объектами Web-страниц) и программ на языке Java. Наш аплет мы назовем просто - Applet1, однако еще раз напоминаю о необходимости присваивать разумные имена. Параметры, указанные в полях Width и Height, ответственны за изначальный размер окна аплета, а параметры в полях HSpace и VSpace - за промежуток между окном аплета и соседними с ним элементами страницы Web, список Align предложит варианты выравнивания. Поле Codebase опишем отдельно. Его значение не имеет отношения к геометрии. Им задается местоположение файла класса аплета. Это может быть полный URL-адрес сервера с подкаталогом, где лежит откомпилированный файл загружаемого аплета, или же путь к нему относительно Web-страницы, через которую аплет запускается. В простейшем случае, когда и аплет, и его страница располагаются в одном каталоге, в поле Codebase нужно ввести символ "." (текущий каталог) или же сделать его пустым. Когда все поля заполнены, остается лишь нажать кнопку Finish, чтобы сгенерировать проект аплета со всеми его файлами.

В левой верхней панели AppBrowser отобразится дерево только что созданного аплета:


Applet1 will appear below in a Java enabled browser.

Обратите внимание: JBuilder, генерируя исходный текст Web-страницы, учел наши локальные настройки и указал в метапараметре заголовка выбранную нами кириллическую кодировку символов ANSI 1251:

Кроме того, JBuilder корректно прописывает имя класса аплета:

CODE = "Applets.Applet1.class"

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

Если теперь переключиться на файл Applet1.java с исходным текстом аплета, то вы обнаружите перекрытый метод getParameter(), который и занят извлечением параметров из HTML-страницы:

public String getParameter(String key, String def) {
  return isStandalone ? System.getProperty(key, def) :
      (getParameter(key) != null ? getParameter(key) : def);
}

Обратите внимание, что при запуске аплета в качестве самостоятельного приложения (вспомните отмечаемую кнопку Can run standalone) для чтения параметров вместо общепринятого метода getParameter() применяется чтение системных свойств (properties). Но это еще не все. Внутрь метода init() среда JBuilder добавляет строку, которая вызовет перекрытый метод getParameter(), а полученный результат приведет к заданному нами ранее типу параметра:

try { 
  varShowFiles = Boolean.valueOf(
    this.getParameter("ShowFiles", "True")).booleanValue();
} catch (Exception e) { e.printStackTrace(); }

Продолжим работу над примером созданием визуальной части аплета. Щелкните на закладке Design для входа в визуальный дизайнер. Щелкните в палитре компонентов на пиктограмме TreeControl и затем - на панели. Компонент будет размещен в пустой панели аплета. Затем у панели, которую вы можете обнаружить в дереве компонентов под ярлыком this, нужно изменить свойство layout на BorderLayout. Это приведет к тому, что компонент будет растянут на всю панель.

А сейчас самое время заняться самим исходным текстом аплета. Первое, что требуется предпринять, - отыскать корневой элемент файловой системы, поэтому напишем для этого специальный метод:

private String findFSRoot() throws IOException
{
  File appletDir = new File(".");
  String curPath = appletDir.getCanonicalPath();
  String root;
  separator = File.separatorChar;
  int index = curPath.indexOf((int)separator);
  root = curPath.substring(0, index);
  if(root.equals(""))
    root = new String( new char[]{ separator });
  return root;
}

Создадим объект класса File, который будет отображать элемент текущего каталога (скорее всего, это "песочница", т. е. каталог, отведенный системой для безопасного исполнения аплета), поэтому конструктору объекта передается символ ".", принятый во многих ОС как ссылка на текущий каталог. Класс File нужен для того, чтобы воспользоваться его методами, столь удобными для выполнения нашей задачи. Это видно из второй строчки, где вызывается метод getCanonicalPath(), возвращающий полный путь к текущему каталогу. Четвертой строкой определяется символ-разделитель, используемый операционной системой пользователя (а мы понятия не имеем, где запущен аплет) для разделения каталогов при описании пути. Следующая строка производит поиск первого от начала символа-разделителя в пути к текущему каталогу, начиная с которого метод substring() отрежет остаток строки. Полученный начальный фрагмент пути есть не что иное, как обозначение корня файловой системы. Для тех операционных систем, у которых роль корня выполняет символ-разделитель, как, например, Unix, в предпоследней строке значению корня файловой системы присваивается значение символа-разделителя. Полученный корневой элемент возвращается методом.

Теперь мы подобрались к самому интересному месту - напишем метод сканирования файловой системы. Ему в качестве параметров передаются: каталог, с которого нужно начинать, ссылка на компонент TreeControl и ссылка на текущий узел дерева этого компонента:

private void enumDirTree(String dir,
                         TreeControl tree,
                         GraphLocation node)
                           throws IOException
{
  int i;
  String[] list;
  File curDir = new File(dir);
  GraphLocation curNode = ( node == null ) ?
      tree.setRoot(dir):
      tree.addChild(node, curDir.getName());
  list = curDir.list();
  for(i = 0; i < list.length; i++)
  {
    File tmp = new File(curDir, list[i]);
    if(tmp.isDirectory())
    {
      enumDirTree(dir + separator + list[i],
          tree, curNode);
    }
    else
    {
      if(varShowFiles)
        tree.addChild(curNode, list[i]);
      else continue;
    }
    if(node == null) tree.expand(curNode);
  }
}

Сначала метод enumDirTree() создает на базе переданного ему активного каталога объект класса File. Затем происходит проверка переданного в параметре node узла дерева (который представляет собой ссылку на класс GraphLocation) на значение null. Если это так, то метод вызван для корневого каталога, и вызывается метод setRoot() компонента TreeControl, устанавливающий корневой элемент отображаемого дерева. В противном случае считается, что метод enumDirTree() уже был вызван и переданный ему первым параметром каталог является подветвью корня дерева и для его отображения вызывается метод addChild(). Первым параметром ему передается ссылка на текущий узел дерева, а вторым - строка с именем добавляемого элемента. Правда, в процесс извлечения этого имени вовлекается метод getName() класса File. Он выделяет имя файла или каталога из полного имени. Обратите внимание, что и setRoot() и addChild() возвращают после добавления элемента дерева ссылку на него, которую мы сохраняем до поры до времени.

На следующем этапе вызывается еще один интересный метод класса File. Это метод list(), производящий перечисление всех элементов текущего каталога и возвращающий их имена в массиве строк. С этого момента запускается цикл, перебирающий возвращенные элементы. Если очередной элемент массива представляет собой ссылку на каталог, то enumDirTree() рекурсивно вызывает сам себя, задав в качестве текущего каталог, переданный ему через параметр dir с добавленным к нему символом-разделителем и названием каталога, выбранного из переменной list. Последним параметром передается ссылка на последний добавленный узел каталога, сохраненный нами ранее. Затем процесс повторяется.

Если выбранный из массива элемент является простым файлом, нужно проверить значение переменной varShowFiles, в которую при запуске аплета было записано значение параметра ShowFiles. Если оно равно false, то файл пропускается оператором continue и цикл начинает новую итерацию. В противном случае уже знакомый нам метод addChild() добавляет название файла в текущий узел отображаемого дерева компонента TreeControl. И в довершение всего последняя строчка метода проверяет, не корневой ли узел дерева мы обрабатываем. Если это так, то он раскрывается, чтобы были видны ветви-подэлементы корневого узла. Данное добавление - чисто косметическое, но оно придает нашему аплету профессиональный вид.

И вот, наконец, дошел черед до метода enumDirTree(). По мнению автора, лучшего места, где он должен быть вызван, чем метод start(), просто нельзя придумать:

public void start() {
  try
  {
     enumDirTree( findFSRoot(), treeControl1, null );
   }catch (IOException x)
  { x.printStackTrace(); };
}

Здесь, собственно, наверное, все понятно: методу enumDirTree() в качестве первого параметра (текущий каталог) передается возвращенный методом findFSRoot() корневой каталог, второй параметр - это ссылка на компонент treeControl1, которую среда JBuilder поместила в исходный текст аплета в тот момент, когда мы положили компонент TreeControl в панель, находясь в визуальном дизайнере. Последний параметр устанавливается равным null, показывая, что метод вызван для корневого каталога. Если желаете начать перебор с другого каталога, то передайте его полное название в качестве первого параметра метода enumDirTree(), а третий параметр все равно должен оставаться null. Поскольку enumDirTree() плотно работает с операциями ввода-вывода, то он размещается в блоке try-catch, который в случае сбоя перехватит управление.

Запустите откомпилированный аплет, нажав комбинацию клавиш +. Автор намеренно не приводит вид работающего класса. Пусть это будет вашим "домашним заданием": повторите описанный проект и посмотрите результат сами. И еще. Если вы попробуете запустить этот аплет с помощью браузера, то у вас ничего не получится, поскольку менеджер защиты виртуальной машины Java не разрешает читать дисковую информацию на чужой машине. Компания Sun обещала, что в JDK 1.2 будет предусмотрена возможность устанавливать уровни защиты по желанию пользователя. Пока же вы можете воспользоваться утилитой AppletViewer из комплекта JDK (в каталоге JBuilderJavain). Для нее ограничений защиты не существует.

* * *

Итак, мы рассмотрели пример создания аплета средствами JBuilder. Конечно, рекурсия, примененная нами, и частые проверки весьма тормозят выполнение кода. Однако не стоит обращать на это внимания. Главное, чтобы вы поняли, как работает мастер Applet. А на следующем занятии мы поговорим о создании оконных приложений.

621