Электронный магазин на Java и XML

       

Класс для создания таблиц


Класс TallyQues использует интерфейс SAX для обработки документа QResultsSet. В результате этой обработки формируется таблица, в которой указывается, сколько раз встретился тот или иной ответ на данный вопрос. Выбор интерфейса SAX в данном случае очевиден — количество занимаемой памяти не зависит от общего числа ответов.

В листинге 7.26 показаны инструкции импорта, объявления классов, переменных и конструктор для класса TallyQues. Заметим, что мы основываем этот класс на классе Handler-Base пакета org.xml.sax, потому что в нем по умолчанию предусмотрены методы обработки событий. Все, что нам остается сделать, — переписать встроенные обработчики событий так, чтобы они обрабатывали те события, которые представляют для нас интерес.

Для работы класса TallyQues используется хэш-таблица, в которой каждому варианту ответа из полного набора вариантов по всем вопросам отводится свой элемент. Эти элементы являются экземплярами внутреннего класса Counter (см. листинг 7.30). Ключом для каждого элемента является атрибут id тега Ques вместе с атрибутом val тега Qopt.

Также у нас имеется вектор Vector с названием ordered (в котором хранится информация по всем вопросам в том порядке, в котором они следуют в анкете) и хэш-таблица qtext (которая содержит строки Qtext с текстом вопросов, снабженные атрибутами id тегов Quest в качестве ключей). Конструктор использует объект Document, соответствующий нашей анкете, для создания всех этих объектов.

Листинг 7.26. Начало кода класса TallyQues (TallyQues.java)

package com.XmlEcomBook.Chap07;

import java.io.* ; import java.util.* ; import org.w3c.dom.* ; import org.xml.sax.* ; import org.xml.sax.helpers.ParserFactory ; import com.sun.xml.parser.Resolver ;

/* org.xml.sax.HandlerBase is a convenience class that extends java.lang.Object and implements the SAX interfaces implements EntityResolver, DTDHandler, DocumentHandler, ErrorHandler */ public class TallyQues extends HandlerBase { static public String parserClass = "com.sun.xml.parser.Parser" ;


private Hashtable tally = new Hashtable(); // Counters keyed by unique // ordered has a Vector of Counters per question private Vector ordered = new Vector(); private Hashtable qtext = new Hashtable(); // <Qtext> by id

public String tableStyle = "align=\"center\" border=\"3\" " ; public String lastErr = null ; public int resultCt = 0 ; String id ; // <Ques> attribute "id" as detected during parse

// constructor creates the vectors and hashtables to store results // qd is the questionnaire source XML doc public TallyQues( Document qd ){ Element E = qd.getDocumentElement(); NodeList qnl = E.getElementsByTagName("Ques"); int ct = qnl.getLength(); for( int i = 0; i < ct ; i++ ){ Vector quesv = new Vector(); // for this <Ques> ordered.addElement( quesv ); E = (Element)qnl.item(i); // Element is a <Ques> NodeList txn = E.getElementsByTagName("Qtext"); String tx = txn.item(0).getFirstChild().getNodeValue(); // question text String id = E.getAttribute( "id" ); qtext.put( id, tx ); quesv.addElement( id ); // first element of quesv is the id NodeList opt = E.getElementsByTagName("Qopt"); int opct =opt.getLength(); for( int n = 0 ; n < opct ; n++ ){ Element opE = (Element) opt.item(n); String val = opE.getAttribute("val"); String text = opE.getFirstChild().getNodeValue(); Counter cntr = new Counter( id, val, text ); quesv.addElement( cntr ); tally.put( cntr.unique, cntr ); } } }

Обработка снимка опроса



Фактическая обработка файла, содержащего снимок результатов опроса, начинается с метода tallyAns. Как показано в листинге 7.27, работа этого метода состоит из следующих этапов.

Файл открывается как объект org.xml .InputSource.

Создается анализатор в соответствии со строкой parserClass (см. листинг 7.26). Мы используем анализатор из пакета Sun, но вы можете заменить его на любой подходящий вам анализатор.

Объект TallyQues присоединяется к анализатору, с тем чтобы он мог получать вызовы методов обработки событий.



Вызывается метод parse и начинается анализ.

Если не происходит никаких ошибок, то по выполнении метода parse таблица готова. Если происходит ошибка, метод tallyAns возвращает null, а если все прошло успешно, то возвращается переменная ordered.



Листинг 7.27. Метод tallyAns (TallyQues.java)

// srcdoc is complete path to a formatted answer set file public Vector tallyAns(String srcdoc ){ Parser parser ; InputSource input ; try { File f = new File( srcdoc ); input = Resolver.createInputSource( f ); parser = ParserFactory.makeParser( parserClass ); parser.setDocumentHandler( this ); System.out.println("Start parse"); parser.parse( input ); }catch(SAXParseException spe){ StringBuffer sb = new StringBuffer( spe.toString() ); sb.append("\n Line number: " + spe.getLineNumber()); sb.append("\nColumn number: " + spe.getColumnNumber() ); sb.append("\n Public ID: " + spe.getPublicId() ); sb.append("\n System ID: " + spe.getSystemId() + "\n"); lastErr = sb.toString(); ordered = null ; }catch(Exception e){ lastErr = e.toString(); ordered = null ; } return ordered ;

Обработка событий с помощью интерфейса SAX

Теперь рассмотрим метод обработки событий, относящийся к методам интерфейса SAX и представленный в листинге 7.28. Из всех методов интерфейса SAX нам нужен только один — startElement. Для каждого тега Ques мы получаем значение атрибута id, которое используется вместе с тегами Qopt, содержащими варианты ответов на вопрос Ques Для каждого тега Qopt мы создаем строку, которая объединяет значение атрибута id вопроса со значением атрибута val элемента Qopt (варианта ответа) Эта строка используется как ключ для извлечения из хэш-таблицы tally соответствующего объекта класса Counter Таким образом подсчитывается, сколько раз встретился данный вариант ответа на данный вопрос анкеты



Листинг 7.28. Методы обработки событий SAX (TallyQues.java)

// this is the SAX specified "callback" called when the // parser detects an element public void startElement( String name, AttributeList attrib) throws SAXException { if( name.equals("Ques") ){ id = attrib.getValue("id"); } else { if( name.equals("Qopt") ){ String unique = id + ":" + attrib.getValue("val"); Counter cntr = (Counter)tally.get( unique ); if( cntr != null ) cntr.countIt(); } else { if( name.equals("Qresults"))resultCt++ ; } } }



Чтобы объединить всю статистическую информацию, полученную классом TallyQues, используется вектор ordered, в котором для каждого вопроса отведен свой элемент, причем эти элементы следуют в том же порядке, в котором расположены вопросы в исходном XML-сценарии анкеты Каждый такой элемент сам по себе также является вектором, в котором содержится идентификатор вопроса id (строка), а затем следуют объекты класса Counter для каждого из вариантов ответа В хэш-таблице qtext указан текст каждого вопроса, ключом к которому является идентификатор данного вопроса

Форматирование полученных результатов

Рассмотрим теперь, каким образом отображается полученная совокупность данных Метод formatAlly, приведенный в листинге 7 29, выводит для каждого вопроса HTML-таблицу, придерживаясь исходного порядка расположения элементов На рис 7 2 показана одна из таблиц, сформированная в результате опроса, который мы недавно проводили на нашем web-сайте в связи с экзаменом на получение сертификата программиста на Java



Листинг 7.29. Метод formatTally создает таблицу HTML (TallyQues.java)

// assumes that tallyAns was just run public void formatTally(PrintWriter out ){ out.println("<center><h2>" + ordered.size() + " Questions " + resultCt + " Responses</h2></center>"); Enumeration e = ordered.elements(); while( e.hasMoreElements() ){ Vector v = (Vector) e.nextElement(); String id = (String)v.firstElement(); out.println("<center><h2>Question: " + id + "</h2>"); out.println("<p>" + qtext.get(id) + "</p>" ) ; out.println("<table cols=\"3\"" + tableStyle + " >"); out.print("<tr>"); out.print("<th>Val</th><th>Count</th><th>Short Option Text</th>"); out.println("</tr>"); for( int i = 1 ; i < v.size(); i++ ){ Counter c = (Counter) v.elementAt(i); out.print("<tr><td>" + c.val + "</td>"); out.print("<td>" + c.count + "</td>" ); out.println("<td>" + c.text + "</td></tr>"); } out.println("</table></center><br><hr>"); } }



public String toString() { StringBuffer sb = new StringBuffer("TallyQues "); return sb.toString() ; }





Рис. 7.2. Отображение в браузере таблицы, сформированной методом fbrmatTally

Остается рассмотреть еще один компонент класса TallyQues — внутренний класс Counter. Как показано в листинге 7.30, в объект Counter входят идентификатор вопроса, значение атрибута val данного варианта ответа и текст варианта ответа. Этот текст мы ограничили по длине, чтобы таблица оставалась компактной, но вы легко можете снять это ограничение.



Листинг 7.30. Внутренний класс Counter (TallyQues.java)

// counter objects represent a single question/option combo class Counter { public String val ; public String unique ; // <Ques id plus ":" plus <Qopt val public String text ; // the first counterTextLen chars public int count = 0 ;

Counter( String id, String v, String tx ){ val = v ; unique = id + ":" + val ; if( tx.length() > counterTextLen ) { text = tx.substring(0, counterTextLen); } else { text = tx ; } } public void countIt(){ count++ ; }

public String toString(){ return "ID: " + unique + " " + count + " " + text ; } } }

Классы PrepQxm и TallyQues, рассмотренные в предыдущих разделах, можно по-разному использовать для создания таблиц в формате HTML. В нашем случае мы задействуем сервлет QanalyslsServ, описанный в следующем разделе.






Содержание раздела