001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.text.feed; 020 021import java.io.IOException; 022import java.io.InputStream; 023 024import lucee.commons.io.IOUtil; 025import lucee.commons.io.SystemUtil; 026import lucee.commons.io.res.Resource; 027import lucee.commons.lang.StringUtil; 028import lucee.runtime.exp.DatabaseException; 029import lucee.runtime.exp.PageException; 030import lucee.runtime.text.xml.XMLUtil; 031import lucee.runtime.type.Collection; 032import lucee.runtime.type.Collection.Key; 033import lucee.runtime.type.KeyImpl; 034import lucee.runtime.type.Query; 035import lucee.runtime.type.QueryImpl; 036import lucee.runtime.type.Struct; 037import lucee.runtime.type.StructImpl; 038 039import org.xml.sax.Attributes; 040import org.xml.sax.InputSource; 041import org.xml.sax.Locator; 042import org.xml.sax.SAXException; 043import org.xml.sax.XMLReader; 044import org.xml.sax.ext.Locator2; 045import org.xml.sax.helpers.DefaultHandler; 046 047public final class RSSHandler extends DefaultHandler { 048 049 public final static String DEFAULT_SAX_PARSER="org.apache.xerces.parsers.SAXParser"; 050 051 private static final Key RSSLINK = KeyImpl.intern("RSSLINK"); 052 private static final Key CONTENT = KeyImpl.intern("CONTENT"); 053 054 private static final Key LINK = KeyImpl.intern("LINK"); 055 private static final Key DESCRIPTION = KeyImpl.intern("DESCRIPTION"); 056 057 private static Collection.Key[] COLUMNS=new Collection.Key[]{ 058 KeyImpl.intern("AUTHOREMAIL"), 059 KeyImpl.intern("AUTHORNAME"), 060 KeyImpl.intern("AUTHORURI"), 061 KeyImpl.intern("CATEGORYLABEL"), 062 KeyImpl.intern("CATEGORYSCHEME"), 063 KeyImpl.intern("CATEGORYTERM"), 064 KeyImpl.intern("COMMENTS"), 065 CONTENT, 066 KeyImpl.intern("CONTENTMODE"), 067 KeyImpl.intern("CONTENTSRC"), 068 KeyImpl.intern("CONTENTTYPE"), 069 KeyImpl.intern("CONTRIBUTOREMAIL"), 070 KeyImpl.intern("CONTRIBUTORNAME"), 071 KeyImpl.intern("CONTRIBUTORURI"), 072 KeyImpl.intern("CREATEDDATE"), 073 KeyImpl.intern("EXPIRATIONDATE"), 074 KeyImpl.intern("ID"), 075 KeyImpl.intern("IDPERMALINK"), 076 KeyImpl.intern("LINKHREF"), 077 KeyImpl.intern("LINKHREFLANG"), 078 KeyImpl.intern("LINKLENGTH"), 079 KeyImpl.intern("LINKREL"), 080 KeyImpl.intern("LINKTITLE"), 081 KeyImpl.intern("LINKTYPE"), 082 KeyImpl.intern("PUBLISHEDDATE"), 083 KeyImpl.intern("RIGHTS"), 084 RSSLINK, 085 KeyImpl.intern("SOURCE"), 086 KeyImpl.intern("SOURCEURL"), 087 KeyImpl.intern("SUMMARY"), 088 KeyImpl.intern("SUMMARYMODE"), 089 KeyImpl.intern("SUMMARYSRC"), 090 KeyImpl.intern("SUMMARYTYPE"), 091 KeyImpl.intern("TITLE"), 092 KeyImpl.intern("TITLETYPE"), 093 KeyImpl.intern("UPDATEDDATE"), 094 KeyImpl.intern("URI"), 095 KeyImpl.intern("XMLBASE") 096 }; 097 098 099 private XMLReader xmlReader; 100 101 private String lcInside; 102 private StringBuffer content=new StringBuffer(); 103 104 private boolean insideImage; 105 private boolean insideItem; 106 107 private Struct image; 108 private Struct properties; 109 private Query items; 110 111 private Collection.Key inside; 112 113 /** 114 * Constructor of the class 115 * @param res 116 * @throws IOException 117 * @throws SAXException 118 * @throws DatabaseException 119 */ 120 public RSSHandler(Resource res) throws IOException, SAXException, DatabaseException { 121 InputStream is=null; 122 try { 123 InputSource source=new InputSource(is=res.getInputStream()); 124 source.setSystemId(res.getPath()); 125 init(DEFAULT_SAX_PARSER,source); 126 } 127 finally { 128 IOUtil.closeEL(is); 129 } 130 } 131 132 /** 133 * Constructor of the class 134 * @param stream 135 * @throws IOException 136 * @throws SAXException 137 * @throws DatabaseException 138 */ 139 public RSSHandler(InputStream stream) throws IOException, SAXException, DatabaseException { 140 InputSource is=new InputSource(IOUtil.getReader(stream, SystemUtil.getCharset())); 141 init(DEFAULT_SAX_PARSER,is); 142 } 143 144 private void init(String saxParser,InputSource is) throws SAXException, IOException, DatabaseException { 145 properties=new StructImpl(); 146 items=new QueryImpl(COLUMNS,0,"query"); 147 xmlReader=XMLUtil.createXMLReader(saxParser); 148 xmlReader.setContentHandler(this); 149 xmlReader.setErrorHandler(this); 150 151 //xmlReader.setEntityResolver(new TagLibEntityResolver()); 152 xmlReader.parse(is); 153 154 //properties.setEL("encoding",is.getEncoding()); 155 156 } 157 158 public void setDocumentLocator(Locator locator) { 159 if (locator instanceof Locator2) { 160 Locator2 locator2 = (Locator2) locator; 161 properties.setEL("encoding", locator2.getEncoding()); 162 } 163 } 164 165 @Override 166 public void startElement(String uri, String name, String qName, Attributes atts) { 167 inside = KeyImpl.getInstance(qName); 168 lcInside=qName.toLowerCase(); 169 if(lcInside.equals("image")) insideImage=true; 170 else if(qName.equals("item")) { 171 items.addRow(); 172 insideItem=true; 173 } 174 else if(lcInside.equals("rss")) { 175 String version = atts.getValue("version"); 176 if(!StringUtil.isEmpty(version)) 177 properties.setEL("version", "rss_"+version); 178 } 179 180 /* / cloud 181 else if(!insideItem && lcInside.equals("cloud")) { 182 183 184 185 String url = atts.getValue("url"); 186 if(!StringUtil.isEmpty(url))items.setAtEL("LINKHREF", items.getRowCount(), url); 187 String length = atts.getValue("length"); 188 if(!StringUtil.isEmpty(length))items.setAtEL("LINKLENGTH", items.getRowCount(), length); 189 String type = atts.getValue("type"); 190 if(!StringUtil.isEmpty(type))items.setAtEL("LINKTYPE", items.getRowCount(), type); 191 }*/ 192 193 194 // enclosure 195 else if(insideItem && lcInside.equals("enclosure")) { 196 String url = atts.getValue("url"); 197 if(!StringUtil.isEmpty(url))items.setAtEL("LINKHREF", items.getRowCount(), url); 198 String length = atts.getValue("length"); 199 if(!StringUtil.isEmpty(length))items.setAtEL("LINKLENGTH", items.getRowCount(), length); 200 String type = atts.getValue("type"); 201 if(!StringUtil.isEmpty(type))items.setAtEL("LINKTYPE", items.getRowCount(), type); 202 } 203 204 else if(atts.getLength()>0) { 205 int len=atts.getLength(); 206 Struct sct=new StructImpl(); 207 for(int i=0;i<len;i++) { 208 sct.setEL(atts.getQName(i), atts.getValue(i)); 209 } 210 properties.setEL(inside, sct); 211 } 212 213 //<enclosure url="http://www.scripting.com/mp3s/weatherReportDicksPicsVol7.mp3" length="6182912" type="audio/mpeg"/> 214 } 215 216 @Override 217 public void endElement(String uri, String name, String qName) { 218 setContent(content.toString().trim()); 219 content=new StringBuffer(); 220 inside=null; 221 lcInside=""; 222 223 if(qName.equals("image")) insideImage=false; 224 if(qName.equals("item")) insideItem=false; 225 } 226 227 228 @Override 229 public void characters (char ch[], int start, int length) { 230 content.append(new String(ch,start,length)); 231 } 232 233 private void setContent(String value) { 234 if(StringUtil.isEmpty(lcInside)) return; 235 236 if(insideImage) { 237 if(image==null){ 238 image=new StructImpl(); 239 properties.setEL("image",image); 240 } 241 image.setEL(inside,value); 242 } 243 else if(insideItem) { 244 try { 245 items.setAt(toItemColumn(inside), items.getRowCount(), value); 246 } catch (PageException e) { 247 //print.err(inside); 248 } 249 250 } 251 else { 252 if(!(StringUtil.isEmpty(value,true) && properties.containsKey(inside))) 253 properties.setEL(inside,value); 254 } 255 } 256 257 private Collection.Key toItemColumn(Collection.Key key) { 258 if(key.equalsIgnoreCase(LINK)) return RSSLINK; 259 else if(key.equalsIgnoreCase(DESCRIPTION))return CONTENT; 260 return key; 261 } 262 263 /** 264 * @return the properties 265 */ 266 public Struct getProperties() { 267 return properties; 268 } 269 270 /** 271 * @return the items 272 */ 273 public Query getItems() { 274 return items; 275 } 276 277 278 /*public static void main(String[] args) throws IOException, SAXException { 279 ResourceProvider frp = ResourcesImpl.getFileResourceProvider(); 280 Resource res = frp.getResource("/Users/mic/Projects/Lucee/webroot/jm/feed/092.xml"); 281 RSSHandler rss=new RSSHandler(res); 282 print.out(rss.getProperties()); 283 print.out(rss.getItems()); 284 285 }*/ 286}