Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Секреты программирования для Internet на Java

.pdf
Скачиваний:
181
Добавлен:
02.05.2014
Размер:
3.59 Mб
Скачать

Класс FIFO

Класс FIFO - это расширение класса Vector, доступного в API. Vector представляет собой класс расширяемого массива в пакете java.util. Мы добавляем некоторые методы для соответствия структуре данных FIFO. FIFO используется при формировании интерфейса пользователя, что сильно облегчает отслеживание данных этого интерфейса. Мы можем помещать объекты в FIFO в особом порядке и доставать их обратно в том же самом порядке, используя соответствующие методы push и pop. У нас также есть метод isEmpty, который проверяет, является ли список пустым:

import java.util.Vector; import java.util.*;

public class FIFO extends Vector { public String pop() {

String s="";

// берем первый элемент

try{ s= (String)firstElement(); } catch (NoSuchElementException e )

{System.out.println("FIFO EMPTY!!!!!"); s="";

}

// и удаляем первый элемент try{ removeElement(s);}

catch(ArrayIndexOutOfBoundsException e) {System.out.println("FIFO EMPTY!!!");}

return s;

}

//добавляем элемент в верх списка public void push(String s) {

addElement(s);

}

//проверяем список на пустоту public boolean empty() {

return isEmpty();

}

Методы, доступные в классе Vector, делают за нас реальную работу. Они генерируют исключения, которые мы должны захватить. Мы определили переменные типа String для элементов, хранящихся в векторе, но с некоторыми небольшими модификациями мы можем заставить FIFO принять любой объект. Мы выбрали тип объекта String, потому что FIFO в нашем апплете используется только со строками.

Получение изображений и описаний

Класс ImagePanel фактически вызывает метод getImage и метод HTTPget, чтобы получить данные о каждом товаре на соответствующей панели. Каждый отдел магазина имеет собственный экземпляр ImagePanel. Мы работам с отделами так, чтобы было легко их изменять, показывая различные панели. Это показано в классе StoreWindow. У нас есть один основной цикл, который повторяется, пока список FIFO, содержащий данные для текущего отдела магазина, не обработан полностью:

import java.awt.*; import java.net.*; import java.util.*; import java.io.*; import ProductImage; import FIFO;

import HTTPget;

public class ImagePanel extends Panel { private ProductImage ImageCanvas; private URL ImageURL;

protected Toolkit Tools;

Ⱦɚɧɧɚɹ ɜɟɪɫɢɹ ɤɧɢɝɢ ɜɵɩɭɳɟɧɚ ɷɥɟɤɬɪɨɧɧɵɦ ɢɡɞɚɬɟɥɶɫɬɜɨɦ %RRNV VKRS Ɋɚɫɩɪɨɫɬɪɚɧɟɧɢɟ ɩɪɨɞɚɠɚ ɩɟɪɟɡɚɩɢɫɶ ɞɚɧɧɨɣ ɤɧɢɝɢ ɢɥɢ ɟɟ ɱɚɫɬɟɣ ɁȺɉɊȿɓȿɇɕ Ɉ ɜɫɟɯ ɧɚɪɭɲɟɧɢɹɯ ɩɪɨɫɶɛɚ ɫɨɨɛɳɚɬɶ ɩɨ ɚɞɪɟɫɭ piracy@books-shop.com

public int width; public int height;

private MediaTracker tracker; private Image i;

public String label; private HTTPget DescText;

Обратите внимание, что в этом классе мы вызываем MediaTracker. Он гарантирует, что изображения получены прежде, чем выполняется ProductImage.setImage. Выполнение с изображением, которое еще не было получено, вызовет NullPointerException. Toolkit необходим, чтобы можно было вызвать getImage, так как мы непосредственно находимся не в панели апплета, а только во фрейме StoreWindow:

public ImagePanel(String ImageURLBase, FIFO FileList, String PanelLabel) { String s;

String Desc;

Toolkit Tools;

String Name; label=PanelLabel; Tools = getToolkit();

tracker= new MediaTracker(this); width=0;

height=0;

setLayout(new GridLayout(0,1)); while (!FileList.empty()) {

s = FileList.pop(); try {

ImageURL = new URL(ImageURLBase + s );

}

catch (MalformedURLException e) { System.out.println("Error retrieving " + ImageURL);

}

Desc = FileList.pop();

DescText = new HTTPget(ImageURL.getHost(), ImageURL.getPort()); DescText.submit( Desc, "");

// получаем изображение

ImageCanvas = new ProductImage(); i = Tools.getImage(ImageURL); tracker.addImage(i, 0);

try {

tracker.waitForID(0);

}

catch (InterruptedException e) { return;

}

Мы используем менеджер размещения GridLayout с одним столбцом и неопределенным числом строк. Так как в различных отделах магазина может быть различное число товаров, мы выбираем такую реализацию для того, чтобы не ограничивать число товаров, которые могут находиться в отделе магазина. Метод pop для FileList FIFO возвращает первый элемент в списке и удаляет его. Три компонента, которые сгруппированы для каждого товара, - имя файла изображения товара, имя файла описания и фактическое имя - анализируются в классе StoreWindow и передаются, когда создан ImagePanel для каждого отдела магазина.

Сначала выбирается файл изображения. Мы формируем URL из параметра, переданного как основной URL, и добавляем путь и имя файла из FileList. Аналогично, мы используем HTTPget, чтобы отыскать файл описания. Мы создаем новые экземпляры ProductImage, называемые ImageCanvas, в цикле для каждого товара. ProductImage создается для каждого товара в отделе. ProductImage также содержит описание и информацию об имени, даже если это объект Canvas. Это сделано для того, чтобы хранить данные о конкретном товаре в одном объекте. Мы назначаем выбранное изображение объекту I и запускаем апплет, чтобы получить изображение немедленно, используя MediaTracker. Конечно, работая с tracker.waitForID(0), мы должны перехватить исключение InterruptedException.

ImageCanvas.setImage(i);

www.books-shop.com

ImageCanvas.setLabel(s);

ImageCanvas.Description=DescText.Results(); Name= FileList.pop(); ImageCanvas.setLabel(Name);

ImageCanvas.resize(ImageCanvas.width,ImageCanvas.height);

add(ImageCanvas);

ImageCanvas.show();

height += ImageCanvas.height; width += ImageCanvas.width;

}

}

} // конец ImagePanel

Теперь, когда мы выбрали все данные по нужным товарам с Web-сервера, давайте поместим их на соответствующее место. Мы используем экземпляры класса ProductImage, чтобы содержать эти данные; во фрагменте кода, приведенном выше, для этого неоднократно приписывается значение ImageCanvas. Мы устанавливаем изображение I, отыскиваем вышеупомянутое изображение с помощью метода setImage, используем метод setLabel, чтобы непосредственно установить метку и описание с помощью переменной Description в классе ProductImage (все через ImageCanvas). Метка или имя является последним членом группы из трех элементов, которые мы выталкиваем от FileList FIFO для специфического товара. Мы используем высоту и ширину изображения товара, чтобы изменить размеры ImageCanvas прежде, чем добавить его к панели ImagePanel. Наконец, мы устанавливаем полный размер ImagePanel так, чтобы его можно было использовать в классе StoreWindow. Экземпляры ImagePanel, представляя различные отделы магазина, добавлены как групповой компонент непосредственно к фрейму StoreWindow.

Обработка действий пользователя

Класс Basket - наша реализация "корзины для покупок". В дополнение к списку товаров, которые пользователь выбрал для приобретения, можно удалить товары из корзины. Корзина состоит из четырех основных элементов: списка и трех кнопок. Она расширяет панель так, чтобы ее можно было добавлять непосредственно к фрейму StoreWindow. Мы обрабатываем функции, которые работают непосредственно со списком внутри этого класса (кнопки "Remove" и "Clear all items"), и передаем нажатие кнопки "Check out of Store" родительскому StoreWindow:

import java.awt.*;

public class Basket extends Panel { private List ItemList; private Button Remove; private Button Clear; public Button Checkout;

public Basket(String Name) {

Label BasketLabel = new Label(Name, Label.CENTER); GridBagConstraints Con = new GridBagConstraints(); GridBagLayout gridBag = new GridBagLayout(); ItemList = new List(5, false);

Remove = new Button("Remove");

Clear = new Button("Clear all items"); Checkout = new Button("Check out of Store"); setLayout(gridBag);

setFont(new Font("Helvetica", Font.PLAIN, 12)); setBackground(Color.blue);

Con.anchor = GridBagConstraints.CENTER; Con.fill = GridBagConstraints.NONE; Con.weighty = 1.0;

Con.weightx=0.0;

Con.gridwidth = GridBagConstraints.REMAINDER; gridBag.setConstraints(BasketLabel, Con); add(BasketLabel);

Con.fill = GridBagConstraints.BOTH; Con.gridheight=3; Con.gridwidth=GridBagConstraints.RELATIVE; Con.weighty = 1.0;

www.books-shop.com

Con.weightx=3.0; gridBag.setConstraints(ItemList, Con); add(ItemList);

Con.weightx=1.0;

Con.weighty = 0.0;

Con.gridheight=1;

Con.gridwidth=GridBagConstraints.REMAINDER; Con.fill = GridBagConstraints.HORIZONTAL; gridBag.setConstraints(Remove, Con); add(Remove);

gridBag.setConstraints(Clear, Con); add(Clear);

Con.fill = GridBagConstraints.BOTH; gridBag.setConstraints(Checkout, Con); add(Checkout);

}

Здесь мы реализуем три кнопки и список, но затеняем (или объявляем с модификатором private) все из них за исключением кнопки "Check out of Store". Это сделано для того, чтобы к ней мог обратиться родительский класс StoreWindow. Функции, обрабатывающие другие кнопки, расположены в самом классе Basket. Обратите внимание, что список создан так, чтобы позволить выбрать только один экземпляр. Мы используем менеджер размещения GridBagLayout для установки ItemList в режиме использования максимума места на панели. Кнопка "Check out of Store" занимает место, остающееся после того, как добавлены кнопки "Remove" и "Clear all items". Детальное обсуждение менеджера размещения GridBagLayout см. в главе 8, "Еще об интерфейсе пользователя".

public void addItem( String ItemName ) { ItemList.addItem(ItemName); ItemList.select(ItemList.countItems()-1);

}

public String getItems() { String s = "";

for (int i=0; i<ItemList.countItems(); i++ ) { s += ItemList.getItem(i)+"\n";

}

return s;

}

public boolean action( Event evt, Object obj)

{

if ("Remove".equals(obj))

{

if (ItemList.getSelectedIndex()!=-1)

{

ItemList.delItem(ItemList.getSelectedIndex());

}

return true;

}

else if ("Clear all items".equals(obj))

{

ItemList.clear(); return true;

}

return super.action(evt, obj); } // конец action

}

Далее мы разрабатываем два метода, addItem и getItems, чтобы позволить добавлять элементы к списку и выбирать список. Эти методы используются в StoreWindow, когда пользователь дважды щелкает по изображению товара или хочет покинуть магазин (мы обсудим это позже, когда доберемся до раздела, описывающего класс StoreWindow). Соответствующие обращения добавляются к методу ItemList.

Обработчики событий для кнопок "Remove" и "Clear all items" расположены внутри класса и заменяют метод action. Мы проверяем, не соответствует ли переданный параметр obj метке одной

www.books-shop.com

из этих двух кнопок, чтобы не передать событие родительскому классу через оператор super.action(evt, obj). Это делается, прежде всего, потому, что мы хотим посылать родительскому классу только событие "Check out of Store". ItemList обрабатывает выбор из списка самостоятельно, так что с ним ничего делать не нужно.

Считывание данных о конфигурации и инициализация

Класс Store - основа апплета. Он вызывает класс StoreWindow, который является фреймом вне броузера Web. Магазин вызывается из Web-страницы с тегом <APPLET>. Главная цель класса Store состоит в том, чтобы получать и анализировать файлы конфигурации Java-магазина для использования их классом StoreWindow. Мы выбираем idx-файлы, начиная с файла Store.idx и затем рекурсивно получая остальные требуемые idx-файлы из указанных подкаталогов:

import java.awt.*;

import java.applet.Applet; import StoreWindow;

import java.util.StringTokenizer; public class Store extends Applet {

StoreWindow f; String ProductDir; String CheckOutCGI;

// Мы хотим поместить его в тег <PARAM>! String Tree = "";

public void init(){

ProductDir = getParameter("index_location"); CheckOutCGI = getParameter("CGI"); ParseIndex( ProductDir, "Store.idx");

f = new StoreWindow(getDocumentBase(), Tree, CheckOutCGI );

}

Мы создаем новый экземпляр класса StoreWindow и получаем указатель на расположение первого файла конфигурации Store.idx (из параметра index_location HTML-кода, из которого вызывался апплет). Мы также получаем URL CGI-программы, чтобы выполнить ее, когда пользователь покинет магазин. Путь будет передан методу ParseIndex, который получает и анализирует все idx-файлы. Кроме того, мы передадим "Store.idx" как заданное по умолчанию имя индексного файла:

private void ParseIndex ( String ProductDir, String s )

{

HTTPget Index; String next;

String nextDESCRIPTION; String nextNAME; String Params = "";

String ProductIndex = ProductDir + s;

Index = new HTTPget(getDocumentBase().getHost(), getDocumentBase().getPort());

Index.submit( ProductIndex, ""); // получаем idx-файл с Web-сервера

StringTokenizer LoadINDEX = new StringTokenizer(Index.Results(),

"\n");

// обрыв idx-файла по "\n"

while (LoadINDEX.hasMoreTokens()) {

// просмотр данного idx-файла, пока в нем что-то есть next= LoadINDEX.nextToken();

//если это не новый каталог, разбираем его и

//возвращаемся в начало, или вызываем ParseIndex

//на этом idx-файле

if (next.endsWith("/")) {

nextDESCRIPTION = LoadINDEX.nextToken(); Tree += ("Menu:" + nextDESCRIPTION +"\n"); // отрезаем остаток после / и добавляем .idx

www.books-shop.com

ParseIndex(ProductDir + next, next.substring(0, (next.length()-1)) + ".idx");

// мы используем рекурсию, так что будьте внимательны!

}

else if (! next.equalsIgnoreCase("<none>") )

{

nextDESCRIPTION = LoadINDEX.nextToken(); nextNAME = LoadINDEX.nextToken();

Tree+= ("GIF:" + ProductDir + next + "\nTXT:" + ProductDir + nextDESCRIPTION + "\n"+"NAME:"+nextNAME+"\n");

}

else {

Tree+=("Encountered a " + next+"\n" );

}

}

Tree += "END:MENU\n";

}

Мы начинаем с методов URL апплета, getDocumentBase.getHost и getDocumentBase.getPort,

чтобы использовать класс HTTPget для получения индексного файла с Web-сервера. Объект Index HTTPget теперь позволяет нам добраться до idx-файла методом Results. HTTPget вызывается неоднократно для различных idx-файлов, пока все они не будут получены.

Затем с помощью StringTokenizer мы разбиваем полученные idx-файлы на части по символам новой строки \n, так как при структурировании idx-файлов мы помещали данные в отдельные строки. Теперь в idx-файле может находиться один из четырех элементов: меню или подменю (тогда мы были бы должны получить другой idx-файл), или имя gif-файла, имя файла описания или идентификатор. Порядок элементов нам известен, так что мы должны искать конечный "/", чтобы определить меню или подменю. Если это - меню или подменю, мы вызываем ParseIndex с путем каталога, который является просто текущим токеном, и добавляем метку, которую мы получаем из следующей строки в idx-файле, разграничивая их отметкой "Menu:". Мы добавляем все это к нашей строковой переменной Tree и передаем StoreWindow, который затем анализирует этот предварительно отформатированный список, чтобы сформировать интерфейс пользователя. Рекурсивное обращение ParseIndex анализирует эти меню, используя те же самые методы ParseIndex, но с другими параметрами. Такое использование рекурсии предохраняет нас от необходимости выполнения вложенных циклов с введением контрольных точек.

Если токен не является каталогом (то есть меню), то он представляет собой товар. Мы ставим отметку, идентифицирующую элемент, добавляемый к Tree, - "GIF:", "TXT:" и "NAME:". Мы будем добавлять эти три части для каждого товара, а отметки позволят нам отладить проблемы с idxфайлами или формированием списка Tree. Мы зацикливаемся внутри каждого idx-файла, пока его обработка не будет завершена, но проводим ветвление несколько раз, когда рекурсивно обрабатываем меню и подменю. Результирующая переменная, строка Tree, может выглядеть запутанной, но мы используем подобный рекурсивный метод в StoreWindow для формирования интерфейса пользователя, и этому методу очень удобно работать с нелинейной формой конфигурации дерева, которую мы здесь создали.

Когда файлы конфигурации проанализированы и помещены в форматированный список Tree, мы создаем новый StoreWindow (getDocumentBase, Tree), которому передаем текущую конфигурацию переменной Tree. Мы также передаем базовый URL так, чтобы можно было использовать его для получения с Web-сервера GIF-файлов изображения и файлов описания для соответствующих товаров.

Объединяем все вместе

Мы соединяем компоненты и добавляем высокоуровневые функциональные возможности в классе StoreWindow. У нас есть предварительно отформатированные данные конфигурации магазина в переменной Tree и все необходимые классы, чтобы начать создавать интерфейс пользователя. Сначала мы устанавливаем соответствующий базовый URL, чтобы получать изображения и файлы описания с Web-сервера. После этого мы создаем требуемые экранные элементы, например корзину для покупок и основанную на менеджере размещения CardLayout панель изображений. Позже, когда мы сформируем интерфейс пользователя и меню, мы отобразим эти панели на экране.

import java.awt.*; import java.net.URL; import java.util.*;

www.books-shop.com

class StoreWindow extends Frame { Panel ProductPicPanel; String Params = "";

Panel DescriptionPanel; String BaseURL;

Panel MainPanel;

TextArea DescArea; Basket ShoppingBasket; String NewPickMessage; String CGI;

public StoreWindow(URL BURL, String Tree, String CGIprogram ) { this.CGI=CGIprogram;

if (BURL.getPort()==-1) {

BaseURL = BURL.getProtocol() +"://" + BURL.getHost();

}

else

{

BaseURL = BURL.getProtocol() +"://" + BURL.getHost() +":"+BURL.getPort() ;

}

ShoppingBasket = new Basket("Shopping Basket"); setTitle("The JavaStore");

MainPanel = new Panel(); MainPanel.setLayout(new BorderLayout()); setLayout(new BorderLayout());

DescArea = new TextArea(15, 30); DescArea.setEditable(false); DescArea.setBackground(Color.black); DescArea.setForeground(Color.white); DescArea.setFont(new Font("Helvetica", Font.PLAIN, 12));

NewPickMessage="Click on a book to see its descrition\nDouble click on a book to

add it to your shopping basket\nChoose from the Store Menu to see more books\n";

MainPanel.add("North", DescArea); MainPanel.add("South", ShoppingBasket); MenuBar Bar = new MenuBar(); ProductPicPanel = new Panel(); ProductPicPanel.setLayout(new CardLayout());

Приведенный выше код главным образом инициализирует фрейм и создает некоторые из нужных нам объектов AWT. Мы устанавливаем менеджеры размещения для основной области и

ProductPicPanel. Мы используем CardLayout для ProductPicPanel, так как мы хотим изменять отображаемую панель, когда выбор меню меняется. Мы добавляем полосу меню, а элементы меню мы добавим несколько позже. Далее, мы вызываем метод BuildUI, который формирует меню и добавляет панели ProductPicPanel. Обратите внимание, что мы уже добавили объекты, которые должны быть в MainPanel. Позже мы добавим MainPanel и ProductPlcPanel к фрейму StoreWindow.

Bar.add(BuildUI(Tree));

Menu Help = new Menu("Help"); Help.add(new MenuItem("About us"));

Help.add(new MenuItem("Ordering information")); Help.add(new MenuItem("-"));

Help.add(new MenuItem("Email us")); Bar.setHelpMenu(Help); Bar.add(Help);

setMenuBar(Bar);

setFont(new Font("Helvetica", Font.PLAIN, 12)); setBackground(Color.gray);

add("Center", MainPanel); add("West", ProductPicPanel); add("Center", MainPanel); resize(700,500);

show();

www.books-shop.com

} // конец StoreWindow

BuildUI возвращает меню, которое мы добавляем непосредственно к полосе меню фрейма. Кроме того, мы добавляем меню "Help" и создаем элементы меню. Мы теперь готовы добавить

ProductPicPanel и MainPanel к фрейму StoreWindow. Метод BuildUI был вызван в этом месте, так что интерфейс пользователя завершен. Ниже мы вносим в список код для метода BuildUI, а также описываем обработку необходимых событий:

public boolean handleEvent(Event evt) {

if (evt.id == Event.WINDOW_DESTROY) { dispose();

return true;

}

if (evt.target instanceof ProductImage) { if (evt.id==Event.MOUSE_DOWN) {

ProductImage p = (ProductImage)evt.target; if (evt.clickCount==2){

ShoppingBasket.addItem(p.label);

}

else {

DescArea.setText(p.Description);

}

}

return true;

}

return super.handleEvent(evt);

}

public boolean action(Event evt, Object arg) { if ( evt.target instanceof MenuItem) {

((CardLayout)ProductPicPanel.getLayout()).show (ProductPicPanel,(String)arg);

DescArea.setText(NewPickMessage); return true;

}

if ("Check out of Store".equals(arg))

{

System.out.println(ShoppingBasket.detItems());

dispose();

SubmitItemList(ShoppingBasket.getItems());

// вызываем метод, чтобы запустить CGI return true;

}

return false;

}

private void SubmitItemList(String ItemList) { // посылаем ItemList CGI-программе

CheckOutFrame Exit = new CheckOutFrame("Check Out of Java Store", ItemList, CGI);

}

Метод handleEvent отслеживает два специфических события - одиночное или двойное нажатие мыши на изображении товара. Мы используем параметр clickCount, чтобы сосчитать число щелчков, и затем переходим на соответствующую обработку. Если пользователь дважды щелкает на изображении, чтобы поместить товар в корзину для покупок, мы вызываем Basket.addItem (ItemName) метод. Как вы, вероятно, помните, мы создали метод addItem, который позволяет добавлять элементы к списку выбранных товаров в классе Basket. Если регистрируется одиночное нажатие, мы показываем описание товара, которое выбираем в классе ImagePanel. Мы заканчиваем handleEvent, передавая остающиеся события обратно так, чтобы следующий обработчик событий мог их обработать.

Обработчик событий сначала проверяет взаимодействие пользователя с меню. Если пользователь выбрал новый элемент меню или отдел магазина, мы показываем панель, которая соответствует этому отделу. При этом переключается сообщение команды, которое нужно отобразить в текстовой области DescArea. Вторая часть обработчика событий проверяет событие нажатия кнопки "Check out of Store", которое передано из ShoppingBasket. Мы получаем список

www.books-shop.com

товаров, выбранных пользователем, и передаем его методу SubmitItemList, который вызывает метод CheckOutofFrame и посылает список CGI-программе, расположенной на Web-сервере. После того как пользователь сделал это, нам больше не нужно оставаться в магазине, поэтому мы закрываем фрейм StoreWindow. CGI-программа будет обрабатывать данные.

private Menu BuildUI(String Tree) { FIFO PanelAdd = new FIFO();

Menu StoreMenu = new Menu("Store Menu");

StringTokenizer SplitTree = new StringTokenizer(Tree, "\n"); ParseTree( SplitTree, StoreMenu, "", PanelAdd);

return StoreMenu; } // конец StoreWindow

private void ParseTree(StringTokenizer TreeBranch, Menu MenuIn, String PreviousToken, FIFO PanelAdd)

{

StringTokenizer Leaf; Menu subMenu;

String next;

// ImagePanel MenuPanel;

while (TreeBranch.hasMoreTokens()) {

Leaf = new StringTokenizer(TreeBranch.nextToken(), ":"); next = Leaf.nextToken();

//ищем "Menu" и добавляем новый элемент в меню if (!PreviousToken.equals("")) {

if (next.equalsIgnoreCase("Menu")) { subMenu = new Menu(PreviousToken);

MenuIn.add(subMenu);

//рекурсивная обработка этого нового подменю

PreviousToken="";

MenuIn=subMenu;

}

else {

MenuIn.add(PreviousToken);

PanelAdd.push(PreviousToken);

}

}

// ищем "Menu" и добавляем новый элемент в меню if (next.equalsIgnoreCase("Menu")) {

ParseTree(TreeBranch, MenuIn, Leaf.nextToken(), PanelAdd);

}

else if (next.equalsIgnoreCase("GIF")) { PanelAdd.push(Leaf.nextToken()); ParseTree(TreeBranch, MenuIn, "", PanelAdd);

}

else if (next.equalsIgnoreCase("TXT")) { PanelAdd.push(Leaf.nextToken()); ParseTree(TreeBranch, MenuIn, "", PanelAdd);

}

else if (next.equalsIgnoreCase("NAME")) { PanelAdd.push(Leaf.nextToken()); ParseTree(TreeBranch, MenuIn, "", PanelAdd);

}

else if (next.equalsIgnoreCase("END")) { if (!PanelAdd.empty())

{

String PanelName =(String)PanelAdd.pop(); Leaf.nextToken(); <%-2>ProductPicPanel.add(PanelName, new

ImagePanel(BaseURL, PanelAdd, PanelName));<%0>

}

}

}

}

} // конец StoreWindow

www.books-shop.com

Метод BuildUI в действительности инициализирует метод ParseTree. Мы создаем новый объект FIFO, новое меню. Затем мы используем StringTokenizer с Tree, отделяя токены по символу новой строки. Tree мы создали в классе Store из данных конфигурации, полученных с Web-сервера.

Оно было предварительно отформатировано и упорядочено так, чтобы рекурсивный метод ParseTree мог его использовать. Большая часть работы в методе BuildUI заключается в правильном использовании рекурсии с методом ParseTree.

Как мы уже говорили, метод ParseTree работает рекурсивно. Он вызывается, когда мы сталкиваемся с новым меню. Это позволяет нам обрабатывать вложенные меню без использования вложенных циклов. Мы вызываем ParseTree со следующими параметрами: StringTokenizer, содержащий дерево, которое должно быть обработано; родительское меню; строка, содержащая предыдущий токен; и список FIFO, содержащий данные для ImagePanel, который должен быть сформирован с этими данными конфигурации. Родительское меню и предыдущий токен требуются для подменю. Вообще мы следуем этим путем в методе ParseTree: обрабатываем текущий лист и получаем следующий лист, который является меткой для предыдущего, затем вызываем ParseTree с остающейся частью дерева. Мы останавливаем разбор дерева, достигнув его конца с отметкой "END". Во время прохода по дереву мы помещаем элементы в буфер FIFO PanelAdd. Этот FIFO-буфер передается ImagePanel, который использует данные в FIFO, чтобы выбрать изображение и описание товара.

Передача выбора пользователя на Web-сервер

Мы вызываем класс CheckOutFrame в методе SubmitItemList. CheckOutFrame состоит из двух текстовых полей и кнопки "Done", как показано на рис. 17-5. Пользователь заполняет поля с именем и телефонным номером. Эти данные наряду со списком выбранных товаров передаются CGI-программе, выполняющейся на Web-сервере классом HTTPpost. Код CheckOutFrame показан ниже. Обратите внимание, что мы уничтожаем StoreWindow перед открытием CheckOutFrame, чтобы не запутать пользователя, и тем самым мы немедленно привлекаем его внимание. Для размещения компонентов мы снова используем менеджер GridBagLayout. Обработчик событий начинает наблюдать за нажатием кнопки Done, по которой мы берем выбранный список товаров, имя пользователя и номер его телефона и посылаем эти данные CGI-программе:

Рис. 17.5.

import java.awt.*; import HTTPpost;

public class CheckOutFrame extends Frame { TextField Name;

TextField Phone; Button Done; String ItemList;

GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints Con = new GridBagConstraints(); Label LName;

Label LPhone;

Label Message1;

www.books-shop.com