001 package railo.runtime.text.feed; 002 003 import java.io.IOException; 004 import java.io.InputStream; 005 import java.util.HashMap; 006 import java.util.Iterator; 007 import java.util.Map; 008 import java.util.Map.Entry; 009 import java.util.Stack; 010 011 import org.xml.sax.Attributes; 012 import org.xml.sax.InputSource; 013 import org.xml.sax.Locator; 014 import org.xml.sax.SAXException; 015 import org.xml.sax.XMLReader; 016 import org.xml.sax.ext.Locator2; 017 import org.xml.sax.helpers.DefaultHandler; 018 import org.xml.sax.helpers.XMLReaderFactory; 019 020 import railo.commons.io.IOUtil; 021 import railo.commons.io.SystemUtil; 022 import railo.commons.io.res.Resource; 023 import railo.commons.lang.StringUtil; 024 import railo.runtime.op.Caster; 025 import railo.runtime.type.Array; 026 import railo.runtime.type.ArrayImpl; 027 import railo.runtime.type.CastableArray; 028 import railo.runtime.type.Collection; 029 import railo.runtime.type.Collection.Key; 030 import railo.runtime.type.KeyImpl; 031 import railo.runtime.type.Struct; 032 import railo.runtime.type.StructImpl; 033 034 public final class FeedHandler extends DefaultHandler { 035 036 public final static String DEFAULT_SAX_PARSER="org.apache.xerces.parsers.SAXParser"; 037 038 039 private XMLReader xmlReader; 040 041 //private StringBuffer content=new StringBuffer(); 042 043 private int deep=0; 044 private FeedStruct data; 045 private String path=""; 046 private Collection.Key inside; 047 private Stack<FeedStruct> parents=new Stack<FeedStruct>(); 048 private FeedDeclaration decl; 049 private Map<String,String> root=new HashMap<String,String>(); 050 private boolean hasDC; 051 private boolean isAtom; 052 053 private boolean inAuthor; 054 055 private boolean inEntry; 056 057 058 /** 059 * Constructor of the class 060 * @param res 061 * @throws IOException 062 * @throws SAXException 063 */ 064 public FeedHandler(Resource res) throws IOException, SAXException { 065 InputStream is=null; 066 try { 067 InputSource source=new InputSource(is=res.getInputStream()); 068 source.setSystemId(res.getPath()); 069 070 init(DEFAULT_SAX_PARSER,source); 071 } 072 finally { 073 IOUtil.closeEL(is); 074 } 075 } 076 public FeedHandler(InputSource is) throws IOException, SAXException { 077 init(DEFAULT_SAX_PARSER,is); 078 079 } 080 081 /** 082 * Constructor of the class 083 * @param stream 084 * @throws IOException 085 * @throws SAXException 086 */ 087 public FeedHandler(InputStream stream) throws IOException, SAXException { 088 InputSource is=new InputSource(IOUtil.getReader(stream, SystemUtil.getCharset())); 089 init(DEFAULT_SAX_PARSER,is); 090 } 091 092 private void init(String saxParser,InputSource is) throws SAXException, IOException { 093 //print.out("is:"+is); 094 hasDC=false; 095 data=new FeedStruct(); 096 xmlReader=XMLReaderFactory.createXMLReader(saxParser); 097 xmlReader.setContentHandler(this); 098 xmlReader.setErrorHandler(this); 099 xmlReader.setDTDHandler(new DummyDTDHandler()); 100 xmlReader.parse(is); 101 } 102 103 /** 104 * @return the hasDC 105 */ 106 public boolean hasDC() { 107 return hasDC; 108 } 109 public void setDocumentLocator(Locator locator) { 110 if (locator instanceof Locator2) { 111 Locator2 locator2 = (Locator2) locator; 112 root.put("encoding", locator2.getEncoding()); 113 } 114 } 115 116 /** 117 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) 118 */ 119 public void startElement(String uri, String name, String qName, Attributes atts) { 120 deep++; 121 122 123 if("entry".equals(name))inEntry=true; 124 else if("author".equals(name))inAuthor=true; 125 126 if(qName.startsWith("dc:")){ 127 name="dc_"+name; 128 hasDC=true; 129 } 130 //print.o("iniside("+deep+"):"+name+"->"+uri); 131 132 133 inside = KeyImpl.getInstance(name); 134 if(StringUtil.isEmpty(path))path=name; 135 else { 136 path+="."+name; 137 } 138 if(decl==null){ 139 String decName=name; 140 String version = atts.getValue("version"); 141 if("feed".equals(decName)) { 142 if(!StringUtil.isEmpty(version))decName="atom_"+version; 143 else decName="atom_1.0"; 144 } 145 else { 146 if(!StringUtil.isEmpty(version))decName+="_"+version; 147 } 148 decl=FeedDeclaration.getInstance(decName); 149 root.put("version",decName); 150 isAtom=decl.getType().equals("atom"); 151 152 } 153 154 155 FeedStruct sct=new FeedStruct(path,inside,uri); 156 157 // attributes 158 Map<String,String> attrs = getAttributes(atts, path); 159 if(attrs!=null){ 160 Entry<String, String> entry; 161 Iterator<Entry<String, String>> it = attrs.entrySet().iterator(); 162 sct.setHasAttribute(true); 163 while(it.hasNext()){ 164 entry = it.next(); 165 sct.setEL(entry.getKey(), entry.getValue()); 166 } 167 } 168 169 // assign 170 if(!isAtom || deep<4) { 171 Object obj = data.get(inside, null); 172 if(obj instanceof Array) { 173 ((Array)obj).appendEL(sct); 174 } 175 else if(obj instanceof FeedStruct){ 176 Array arr = new ArrayImpl(); 177 arr.appendEL(obj); 178 arr.appendEL(sct); 179 data.setEL(inside, arr); 180 } 181 else if(obj instanceof String){ 182 // wenn wert schon existiert wird castableArray in setContent erstellt 183 } 184 else { 185 El el=(El) decl.getDeclaration().get(path); 186 if(el!=null && (el.getQuantity()==El.QUANTITY_0_N || el.getQuantity()==El.QUANTITY_1_N)){ 187 Array arr = new ArrayImpl(); 188 arr.appendEL(sct); 189 data.setEL(inside, arr); 190 } 191 else data.setEL(inside, sct); 192 193 } 194 } 195 parents.add(data); 196 data=sct; 197 198 //<enclosure url="http://www.scripting.com/mp3s/weatherReportDicksPicsVol7.mp3" length="6182912" type="audio/mpeg"/> 199 } 200 201 202 public void endElement(String uri, String name, String qName) { 203 if("entry".equals(name))inEntry=false; 204 else if("author".equals(name))inAuthor=false; 205 deep--; 206 if(isAtom && deep>=(inEntry && inAuthor?4:3)) { 207 String content = data.getString(); 208 Key[] keys = data.keys(); 209 StringBuilder sb=new StringBuilder(); 210 sb.append("<"); 211 sb.append(qName); 212 213 // xmlns 214 if(!parents.peek().getUri().equals(uri)) { 215 sb.append(" xmlns=\""); 216 sb.append(uri); 217 sb.append("\""); 218 } 219 220 for(int i=0;i<keys.length;i++){ 221 sb.append(" "); 222 sb.append(keys[i].getString()); 223 sb.append("=\""); 224 sb.append(Caster.toString(data.get(keys[i],""),"")); 225 sb.append("\""); 226 227 } 228 229 230 if(!StringUtil.isEmpty(content)) { 231 sb.append(">"); 232 sb.append(content); 233 sb.append("</"+qName+">"); 234 } 235 else sb.append("/>"); 236 237 238 239 240 241 242 data= parents.pop(); 243 data.append(sb.toString().trim()); 244 //setContent(sb.toString().trim()); 245 246 path=data.getPath(); 247 inside=data.getInside(); 248 return; 249 } 250 251 252 253 //setContent(content.toString().trim()); 254 setContent(data.getString().trim()); 255 256 257 258 data=(FeedStruct) parents.pop(); 259 path=data.getPath(); 260 inside=data.getInside(); 261 } 262 263 public void characters (char ch[], int start, int length) { 264 data.append(new String(ch,start,length)); 265 //content.append(new String(ch,start,length)); 266 } 267 268 private void setContent(String value) { 269 //print.out(path+":"+inside); 270 if(StringUtil.isEmpty(inside)) return; 271 272 if(data.hasAttribute()) { 273 if(!StringUtil.isEmpty(value))setEl(data,KeyImpl.VALUE,value); 274 } 275 else { 276 FeedStruct parent=(FeedStruct) parents.peek(); 277 setEl(parent,inside,value); 278 } 279 280 } 281 282 private void setEl(Struct sct, Collection.Key key, String value) { 283 Object existing = sct.get(key,null); 284 285 if(existing instanceof CastableArray){ 286 ((CastableArray)existing).appendEL(value); 287 } 288 else if(existing instanceof String){ 289 CastableArray ca=new CastableArray(existing); 290 ca.appendEL(existing); 291 ca.appendEL(value); 292 sct.setEL(key,ca); 293 } 294 else 295 sct.setEL(key,Caster.toString(value)); 296 297 298 /*if(existing instanceof Struct)sct.setEL(key,value); 299 else if(existing instanceof Array)((Array)existing).appendEL(value); 300 else if(existing!=null){ 301 CastableArray ca=new CastableArray(existing); 302 ca.appendEL(existing); 303 ca.appendEL(value); 304 sct.setEL(key,ca); 305 } 306 else*/ 307 } 308 309 private Map<String,String> getAttributes(Attributes attrs, String path) { 310 El el = (El) decl.getDeclaration().get(path); 311 312 int len=attrs.getLength(); 313 if((el==null || el.getAttrs()==null) && len==0) return null; 314 315 Map<String,String> map=new HashMap<String,String>(); 316 if(el!=null) { 317 Attr[] defaults = el.getAttrs(); 318 if(defaults!=null) { 319 for(int i=0;i<defaults.length;i++) { 320 if(defaults[i].hasDefaultValue()) 321 map.put(defaults[i].getName(), defaults[i].getDefaultValue()); 322 } 323 } 324 } 325 for(int i=0;i<len;i++) { 326 map.put(attrs.getQName(i), attrs.getValue(i)); 327 } 328 return map; 329 } 330 331 332 /** 333 * @return the properties 334 */ 335 public Struct getData() { 336 return data; 337 } 338 339 340 341 342 /** 343 * @see org.xml.sax.helpers.DefaultHandler#endDocument() 344 */ 345 public void endDocument() throws SAXException { 346 super.endDocument(); 347 Struct def=new StructImpl(); 348 Key[] entryLevel = decl.getEntryLevel(); 349 for(int i=0;i<entryLevel.length;i++) { 350 data=(FeedStruct) data.get(entryLevel[i],def); 351 } 352 data.putAll(root); 353 } 354 355 356 }