Source code: com/fm/rss/fsChannelStorage.java
1 /****************************************************************************
2 * Copyright (c) 2003 Andrew Duka | aduka@users.sourceforge.net
3 * All right reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 ****************************************************************************/
11 package com.fm.rss;
12
13 import java.util.*;
14 import java.io.*;
15
16 import org.w3c.dom.Element;
17 import org.w3c.dom.Node;
18 import org.w3c.dom.NodeList;
19 import org.xml.sax.SAXException;
20
21 import javax.xml.parsers.ParserConfigurationException;
22
23 import com.fm.rss.filter.RssItemFilter;
24
25 /**
26 * File system channel storage.
27 *
28 * <p>Storage will save/load its state to/from the well formed XML document
29 * stored in the file specified by DESTINATION parameter. Load operation
30 * accepts remote resources if they are specified by correct URI.</p>
31 *
32 * <p>Complete format of the storage file may discovered from the FM DTD,
33 * here is the only brief schema:
34 * <code>
35 * <?xml version="1.0">
36 * <rss-channel-storage xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/">
37 * <dcterms:dateCreated> Date when current storage was actually created</dcterms:dateCreated>
38 * <dcterms:dateModified> Date when current storage was last time saved to disk</dcterms:dateModified>
39 *
40 *
41 * //Each category is stored in separate hasPart element
42 * <dcterms:hasPart>
43 * Category string here
44 * </dcterms:hasPart>
45 *
46 * <dcterms:hasPart>
47 * Another category string here
48 * </dcterms:hasPart>
49 *
50 * .....
51 *
52 * </rss-channel-storage>
53 * </code>
54 */
55 public class fsChannelStorage implements ChannelStorage {
56
57 private HashMap params;
58 private HashMap categories;
59 private static final String DEFAULT_SOURCE_NAME = "fm-default-storage.xml";
60
61
62 /**
63 * Default constructor
64 */
65 public fsChannelStorage()
66 {
67 this.params = new HashMap();
68 this.categories = new HashMap();
69 }
70
71 /**
72 * Return list of the channel categories
73 *
74 * @return ArrayList of categories (list is empty if no categories exist)
75 */
76 public Map getCategories()
77 {
78 return this.categories;
79 }
80
81 /**
82 * Returns plain list of the channel entries reqtreived from existing
83 * categories
84 *
85 * @return Map object containing all known channels
86 */
87 public Map getChannels()
88 {
89 return null;
90 }
91
92 /**
93 * Set list of the existing categories to given value
94 *
95 * @param newCats
96 */
97 public void setCategories(Map newCats)
98 {
99 this.categories = new HashMap(newCats);
100 }
101
102 /**
103 * Saves state of the storage to disk.
104 *
105 * The destination file must be specified using <code>setParamValue()</code>,
106 * with <code>ChannelStorage.DESTINATION</code> as a parameter name.</p>
107 *
108 * @throws IOException if I/O error occures during saving
109 */
110 public void save() throws IOException
111 {
112 String destination = "";
113 if ((destination = (String)this.params.get(ChannelStorage.DESTINATION)) == null)
114 {
115 throw new IOException("fsChannelStorage: destination parameter isn't set");
116 }
117
118 try
119 {
120 //FileWriter out_stream = new FileWriter(destination);
121 OutputStreamWriter out_stream = new OutputStreamWriter(new FileOutputStream(destination),
122 "UTF-8");
123 StringBuffer header = new StringBuffer();
124 header.append("<?xml version='1.0'?>");
125 header.append("<rss-channel-storage xmlns:dc='");
126 header.append(documentAdapter.DC_NAMESPACE_URI);
127 header.append("' xmlls:dcterms='");
128 header.append(documentAdapter.DCTERMS_NAMESPACE_URI);
129 header.append("'>");
130 header.append("<dcterms:dateCreated>");
131 header.append(rssDateHandler.dateToString(new Date(), rssDateHandler.RSS_OUTPUT_PATTERN));
132 header.append("</dcterms:dateCreated>");
133 header.append("<dcterms:dateModified>");
134 header.append((new rssDateHandler()).toString());
135 header.append("</dcterms:dateModified>");
136
137 out_stream.write(header.toString());
138
139 String c_s;
140 rssChannelCategory c;
141 RssItemFilter filter;
142
143 if (params.containsKey(ChannelStorage.OUTPUT_FILTER))
144 filter = (RssItemFilter) params.get(ChannelStorage.OUTPUT_FILTER);
145 else
146 filter = null;
147
148
149 // dumpnig categories
150 for (Iterator i = categories.keySet().iterator(); i.hasNext();)
151 {
152 c = (rssChannelCategory) categories.get(i.next());
153
154 if (filter != null)
155 c.filterItems(filter);
156
157 c_s = c.toString();
158 if (c_s != null)
159 out_stream.write(c_s);
160 }
161
162 out_stream.write("</rss-channel-storage>");
163 out_stream.close();
164 }
165 catch (IOException e)
166 {
167 throw e;
168 }
169
170 }
171
172 /**
173 * Loads state of the storage from the source.
174 *
175 * <p>The source file(resource) must be specified using
176 * <code>setParamValue()</code>, with <code>ChannelStorage.SOURCE</code>
177 * as a parameter name. Note that for load operation the source may be a
178 * remote resource specified by URI as well.</p>
179 *
180 * @throws IOException if I/O error occures during saving
181 */
182 public void load() throws IOException, rssParseException
183 {
184 String source = "";
185 this.categories = new HashMap();
186
187 if ((source = (String)this.params.get(ChannelStorage.SOURCE)) == null)
188 {
189 throw new IOException("fsChannelStorage: source isn't set");
190 }
191
192 try
193 {
194 documentAdapter docAdapter = documentAdapter.newInstance((String)this.params.get(ChannelStorage.SOURCE),
195 true,
196 true);
197
198 Element el = docAdapter.getDocumentElement();
199 // nothing to get our state from
200 if (el == null)
201 {
202 throw new rssParseException("document contains no data");
203 }
204
205 // chacking that if we've opened channel file instead of storage
206 String el_title = el.getNodeName();
207
208 if (el_title.equalsIgnoreCase("rss") ||
209 el_title.equalsIgnoreCase("rdf:RDF") ||
210 el_title.equalsIgnoreCase("rss-channel"))
211 {
212 rssChannel op_channel = new rssChannel();
213
214 try
215 {
216 op_channel.parse(el);
217
218 rssChannelCategory new_cat = new rssChannelCategory("Auto created",
219 "This category was created automatically," +
220 "because you selected channel file instead of storage");
221 new_cat.setID(new_cat.generateID());
222 new_cat.addChannel(op_channel);
223 categories.put(new Integer(new_cat.getID()),new_cat);
224 return;
225 }
226 catch (rssParseException pe)
227 {
228 throw new rssParseException("Storage parse error: "+ pe.toString());
229 }
230 }
231
232 Node curr_node;
233 NodeList nl = el.getChildNodes();
234 String node_name;
235 rssChannelCategory new_cat;
236 int child_num = nl.getLength();
237
238 for (int i=0; i < child_num; i++)
239 {
240 curr_node = nl.item(i);
241 if (curr_node.getNodeType() == Node.ELEMENT_NODE)
242 {
243 node_name = curr_node.getNodeName();
244 if (node_name.equalsIgnoreCase("rss-channel-category"))
245 {
246 new_cat = new rssChannelCategory();
247 try
248 {
249 new_cat.parse((Element)curr_node);
250 }
251 catch (rssParseException pe)
252 {
253 throw new rssParseException("Storage parse error: "+ pe.toString());
254 }
255
256 this.categories.put(new Integer(new_cat.getID()),new_cat);
257 }
258 }//end of ELEMENT_NODE
259 }//end of for
260
261 }
262 catch (SAXException sax_e)
263 {
264 sax_e.printStackTrace();
265 throw new rssParseException("Storage parse error: " + sax_e.toString());
266 }
267 catch (ParserConfigurationException e)
268 {
269 e.printStackTrace();
270 throw new IOException("Storage DOM error: " + e.toString());
271 }
272
273
274
275 }
276
277 /**
278 * Set configuration param.
279 *
280 * All unrecognized params are ignored.
281 *
282 * @param param String with parameter name
283 * @param value Object with parameter value
284 */
285 public void setParamValue(String param, Object value)
286 {
287 params.put(param, value);
288 }
289
290 /**
291 * Return value of the configuration param.
292 *
293 * The <code>null</code> will be returned if specified param
294 * doesn't exist.
295 *
296 * @return String with parameter value or <code>null</code> if parameter
297 * doesn't exist.
298 */
299 public Object getParamValue(String param)
300 {
301 return params.get(param);
302 }
303
304 /**
305 * Return name of the default file from which data should be loaded
306 * @return
307 */
308 public Object getDefaultSource()
309 {
310 return fsChannelStorage.DEFAULT_SOURCE_NAME;
311 }
312
313 }