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

       

Класс NewsUpKeep


Классу NewsUpKeep передаются существующий объект DOM <Newsfile> и различные текстовые строки, которые составляют новый элемент <Newsitem>; он переписывает файл XML с сообщениями. Применить этот метод проще, чем создать элемент <Newsitem> и вставить его модель DOM, которая постоянно находится в памяти; кроме того, указанный метод гарантирует, что файл новостей будет обновлен правильным образом. Также при использовании этого метода исключается возможность того, что один из отображающих методов получит частично измененную модель DOM.

В листинге 8.27 показано начало кода класса NewsUpKeep. Конструктор класса использует полное имя файла для получения DOM из DOMIibrary. Поскольку эта модель DOM не модифицируется при переписывании файла XML, вам не нужно беспокоиться по поводу возможности одновременного доступа к этому объекту класса NewsFormatter.

Заметим, что в классе NewsUpKeep создается переменная rootNNM типа NamedNodeMap, которая содержит имена и значения атрибутов корневого элемента документа XML, <Newsfile>. Также конструктор помещает все узлы <Newsitem> в массив itemNodes.

Листинг 8.27. Начало класса NewsUpKeep (NewsUpKeep.java)

package com.XmlEcomBook.Chap08;

import com.XmlEcomBook.DOMlibrary ; import java.io.*; import java.util.* ; import javax.servlet.*; import javax.servlet.http.*; import org.w3c.dom.* ;

public class NewsUpkeep { File newsFile ; String newsFileName ; Node[] itemNodes ; NamedNodeMap rootNNM ; // for root attributes

public NewsUpkeep( File f) throws IOException { newsFile = f ; newsFileName = f.getAbsolutePath() ; DOMlibrary library = DOMlibrary.getLibrary(); Document doc = library.getDOM( newsFileName ); if( doc == null ){ throw new FileNotFoundException( newsFileName ); } Element re = doc.getDocumentElement(); rootNNM = re.getAttributes(); System.out.println("Root has " + rootNNM.getLength() + " attributes"); NodeList newsItemNodes = doc.getElementsByTagName("Newsitem"); int ct = newsItemNodes.getLength(); itemNodes = new Node[ ct ]; for( int i = 0 ; i < ct ; i++ ){ itemNodes[i] = newsItemNodes.item( i ); } }


В листинге 8. 28 показаны некоторые вспомогательные методы, необходимые в классе NewsUpKeep. Метод formatTopics гарантирует, что строка, которая будет записана в качестве значения атрибута topic, имеет правильный формат.



Листинг 8.28. Различные вспомогательные функции класса NewsUpKeep (NewsUpKeep.java)

//ensure there are no leading or trailing spaces on the // individual topics, comma separated, general,food , etc private String formatTopics(String s ){ if( s.indexOf(',') < 0 ) return s.trim(); // only separator is comma StringTokenizer st = new StringTokenizer( s, "," ); StringBuffer sb = new StringBuffer( s.length() ); while( st.hasMoreTokens() ){ sb.append( st.nextToken().trim() ); if( st.hasMoreTokens() ) sb.append(','); } return sb.toString(); } // convert system millisecs to days since epoch private String timeInDays(){ long t = System.currentTimeMillis() ; int tid = (int)(t / ( 1000 * 60 * 60 * 24 )); return Integer.toString( tid ); }



// s expected to be decimal number used in <Newsitem id= private String incrementID(String s ){ try{ int n = Integer.parseInt( s ); return Integer.toString( n + 2 ); }catch(NumberFormatException e){ return s + "a" ; } } public String toString() { StringBuffer sb = new StringBuffer("NewsUpkeep "); sb.append(" Newsitem count: " ); sb.append( Integer.toString( itemNodes.length )); return sb.toString(); }

Теперь мы подходим к основному рабочему методу, addltem. Сначала этот метод создает новый файл с временным именем и записывает туда стандартное объявление XML и комментарии. Затем создается тег <Newsfile>, куда записываются имена атрибутов и их значения из коллекции rootNNM.

Как видно из листинга 8.29, атрибут nextid обрабатывается специальным образом. Сохраняется текущее значение, которое становится значением атрибута id нового элемента <Newsitem>, а увеличенное значение записывается в тег <Newsfile>.

Листинг 8.29. Начало метода addltem (NewsUpKeep.java)

// items are always added at the top of the file // so we have to rebuild the start of the root element public void addItem( String head, String date, String topics, String author, String shrtStr, String longStr ) throws IOException { String idVal = "" ; String tmpfile = newsFileName + "$$" ; File f = new File( tmpfile ); FileWriter fw = new FileWriter(f); PrintWriter out = new PrintWriter( new BufferedWriter( fw ) ); out.println("<?xml version=\"1.0\" standalone=\"yes\" ?>"); out.println("<!-- output by NewsUpkeep -->"); int ct = rootNNM.getLength(); if( ct == 0 ){ out.println("<Newsfile>"); } else { out.print("<Newsfile "); for( int i = 0 ; i < ct ; i++ ){ Node an = rootNNM.item(i); String name = an.getNodeName(); String val = an.getNodeValue(); out.print( name + "=\"" ); if( name.equals("nextid") ){ idVal = val ; val = incrementID( val ); } out.print( val + "\" "); } out.println(" >"); }



Затем, как показано в листинге 8.30, пишется новый тег <NewsItem>, за которым следует заголовок сообщения, указывается дата и приводятся краткая и полная версии текста сообщения. Для того чтобы записать старые элементы <NewsItan>, вызывается метод writeNewsNode. После закрытия временного файла старый файл XML удаляется, а временный файл получает имя. Следующий раз, когда этот файл будет запрошен, класс DOMIibrary по изменившейся метке даты модификации файла (timestamp) определит, что нужно считывать новый файл.



Листинг 8.30. Метод addltem, продолжение (NewsllpKeep.java)

out.print("<Newsitem timestamp=\""); out.print( timeInDays() + "\" topic=\""); out.print( formatTopics( topics ) ); out.println( "\" author=\"" + author + "\" id=\"" + idVal + "\" >"); // end of <Newsitem .. > out.println("<head>" + head.trim() + "</head>" ); out.println("<date>" + date.trim() + "</date>" ); out.println("<short><![CDATA["); out.println( shrtStr.trim() ); out.println("]]></short>"); out.println("<long><![CDATA["); out.println( longStr ); out.println("]]></long>"); out.println("</Newsitem>"); for( int i = 0 ; i < itemNodes.length ; i++ ){ writeNewsNode(out, (Element)itemNodes[i] ); } out.println("</Newsfile>"); out.flush(); out.close(); File forig = new File( newsFileName ); DOMlibrary library = DOMlibrary.getLibrary(); // to prevent overlapping XML file operations synchronized( library ){ forig.delete(); if( !f.renameTo( forig )){ System.out.println("NewsUpkeep.addItem rename failed") ; } } }

Метод writeNewsNode, который записывает отдельный элемент <Newsitem>, показан в листинге 8.31.



Листинг 8.31. Метод, который записывает отдельный элемент из DOM (NewsllpKeep.java)

// write a <Newsitem Element duplicating the attributes public void writeNewsNode(PrintWriter out, Element e) { NamedNodeMap nnm = e.getAttributes(); out.print("<Newsitem " ) ; //timestamp=\""); int i ; for( i = 0 ; i < nnm.getLength() ; i++ ){ Attr na = (Attr) nnm.item(i); // Attr extends Node String atr = na.getName(); String val = na.getValue(); out.print( atr ); out.print("=\""); out.print( val ); out.print("\" "); } out.println(">"); NodeList nl = e.getChildNodes(); int ct = nl.getLength(); for( i = 0 ; i < ct ; i++ ){ Node nde = nl.item( i ); if( nde instanceof Element ){ Element ce = (Element)nde; String name = ce.getTagName(); out.print("<" + name + ">"); NodeList chnl = ce.getChildNodes() ; if( chnl.getLength() == 0 ) continue ; Node chn = chnl.item(0); if( name.equals("long") || name.equals("short") ){ out.print("<![CDATA["); out.println( chn.getNodeValue().trim() ); out.print("]]>"); } else { out.print( chn.getNodeValue() ); } out.println("</" + name + ">"); } } // loop over <Newsitem> child nodes out.println("</Newsitem>"); } }

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


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