001 package railo.runtime.tag; 002 003 import java.io.IOException; 004 import java.io.Reader; 005 import java.io.StringReader; 006 import java.util.ArrayList; 007 008 import org.xml.sax.InputSource; 009 import org.xml.sax.SAXException; 010 011 import railo.commons.io.IOUtil; 012 import railo.commons.io.res.Resource; 013 import railo.commons.io.res.type.http.HTTPResource; 014 import railo.commons.io.res.util.ResourceUtil; 015 import railo.commons.lang.Pair; 016 import railo.commons.lang.StringUtil; 017 import railo.runtime.exp.ApplicationException; 018 import railo.runtime.exp.ExpressionException; 019 import railo.runtime.exp.PageException; 020 import railo.runtime.ext.tag.TagImpl; 021 import railo.runtime.functions.dateTime.GetHttpTimeString; 022 import railo.runtime.net.proxy.ProxyData; 023 import railo.runtime.net.proxy.ProxyDataImpl; 024 import railo.runtime.op.Caster; 025 import railo.runtime.op.Decision; 026 import railo.runtime.text.feed.FeedHandler; 027 import railo.runtime.text.feed.FeedQuery; 028 import railo.runtime.text.xml.XMLUtil; 029 import railo.runtime.type.Array; 030 import railo.runtime.type.List; 031 import railo.runtime.type.Query; 032 import railo.runtime.type.Struct; 033 034 public final class Feed extends TagImpl { 035 036 private static final int ACTION_READ = 0; 037 private static final int ACTION_CREATE = 1; 038 039 private static final int TYPE_AUTO = 0; 040 private static final int TYPE_RSS = 1; 041 private static final int TYPE_ATOM = 2; 042 043 044 045 private int action=ACTION_READ; 046 private Struct columnMap=null; 047 private Resource enclosureDir=null; 048 private boolean ignoreEnclosureError=false; 049 private Object name=null; 050 private Resource outputFile=null; 051 private boolean overwrite=false; 052 private boolean overwriteEnclosure=false; 053 private Object properties=null; 054 private Object query=null; 055 private Resource source=null; 056 private int timeout=-1; 057 private int type=TYPE_AUTO; 058 private String userAgent=null; 059 private String xmlVar=null; 060 061 private String proxyPassword=null; 062 private int proxyPort=80; 063 private String proxyServer=null; 064 private String proxyUser=null; 065 private String charset=null; 066 067 /** 068 * @see railo.runtime.ext.tag.TagImpl#release() 069 */ 070 public void release() { 071 charset=null; 072 action=ACTION_READ; 073 columnMap=null; 074 enclosureDir=null; 075 ignoreEnclosureError=false; 076 name=null; 077 outputFile=null; 078 overwrite=false; 079 overwriteEnclosure=false; 080 properties=null; 081 query=null; 082 source=null; 083 timeout=-1; 084 userAgent=null; 085 xmlVar=null; 086 087 proxyPassword=null; 088 proxyPort=80; 089 proxyServer=null; 090 proxyUser=null; 091 092 type=TYPE_AUTO; 093 super.release(); 094 } 095 /** set the value charset 096 * Character set name for the file contents. 097 * @param charset value to set 098 **/ 099 public void setCharset(String charset) { 100 this.charset=charset.trim(); 101 } 102 103 /** 104 * @param action the action to set 105 * @throws ApplicationException 106 */ 107 public void setAction(String strAction) throws ApplicationException { 108 109 strAction=StringUtil.toLowerCase(strAction.trim()); 110 if("read".equals(strAction)) action=ACTION_READ; 111 else if("create".equals(strAction)) action=ACTION_CREATE; 112 113 else throw new ApplicationException("invalid action definition ["+strAction+"], valid action definitions are " + 114 "[create,read]"); 115 } 116 117 public void setType(String strType) throws ApplicationException { 118 119 strType=StringUtil.toLowerCase(strType.trim()); 120 if("rss".equals(strType)) type=TYPE_RSS; 121 else if("atom".equals(strType)) type=TYPE_ATOM; 122 123 else throw new ApplicationException("invalid type definition ["+strType+"], valid type definitions are " + 124 "[atom,rss]"); 125 } 126 /** 127 * @param columnMap the columnMap to set 128 */ 129 public void setColumnmap(Struct columnMap) { 130 this.columnMap = columnMap; 131 } 132 /** 133 * @param enclosureDir the enclosureDir to set 134 * @throws ExpressionException 135 */ 136 public void setEnclosuredir(String strEnclosureDir) throws ExpressionException { 137 this.enclosureDir = ResourceUtil.toResourceExisting(pageContext, strEnclosureDir); 138 } 139 /** 140 * @param ignoreEnclosureError the ignoreEnclosureError to set 141 */ 142 public void setIgnoreenclosureerror(boolean ignoreEnclosureError) { 143 this.ignoreEnclosureError = ignoreEnclosureError; 144 } 145 /** 146 * @param name the name to set 147 */ 148 public void setName(Object name) { 149 this.name = name; 150 } 151 /** 152 * @param outputFile the outputFile to set 153 * @throws ExpressionException 154 */ 155 public void setOutputfile(String strOutputFile) throws ExpressionException { 156 this.outputFile = ResourceUtil.toResourceExistingParent(pageContext, strOutputFile); 157 } 158 /** 159 * @param overwrite the overwrite to set 160 */ 161 public void setOverwrite(boolean overwrite) { 162 this.overwrite = overwrite; 163 } 164 /** 165 * @param overwriteEnclosure the overwriteEnclosure to set 166 */ 167 public void setOverwriteenclosure(boolean overwriteEnclosure) { 168 this.overwriteEnclosure = overwriteEnclosure; 169 } 170 /** 171 * @param properties the properties to set 172 */ 173 public void setProperties(Object properties) { 174 this.properties = properties; 175 } 176 /** 177 * @param query the query to set 178 */ 179 public void setQuery(Object query) { 180 this.query = query; 181 } 182 /** 183 * @param source the source to set 184 * @throws ExpressionException 185 */ 186 public void setSource(String strSource) throws ExpressionException { 187 // when using toExistingResource execution fails because proxy is missed at this time 188 this.source = ResourceUtil.toResourceNotExisting(pageContext, strSource); 189 } 190 /** 191 * @param timeout the timeout to set 192 */ 193 public void setTimeout(double timeout) { 194 this.timeout = (int) timeout; 195 } 196 /** 197 * @param userAgent the userAgent to set 198 */ 199 public void setUseragent(String userAgent) { 200 this.userAgent = userAgent; 201 } 202 /** 203 * @param xmlVar the xmlVar to set 204 */ 205 public void setXmlvar(String xmlVar) { 206 this.xmlVar = xmlVar; 207 } 208 /** 209 * @param proxyPassword the proxyPassword to set 210 */ 211 public void setProxypassword(String proxyPassword) { 212 this.proxyPassword = proxyPassword; 213 } 214 215 /** 216 * @param proxyPort the proxyPort to set 217 */ 218 public void setProxyport(double proxyPort) { 219 this.proxyPort = (int) proxyPort; 220 } 221 222 /** 223 * @param proxyServer the proxyServer to set 224 */ 225 public void setProxyserver(String proxyServer) { 226 this.proxyServer = proxyServer; 227 } 228 /** 229 * @param proxyUser the proxyUser to set 230 */ 231 public void setProxyuser(String proxyUser) { 232 this.proxyUser = proxyUser; 233 } 234 235 236 237 /** 238 * @see railo.runtime.ext.tag.TagImpl#doStartTag() 239 */ 240 public int doStartTag() throws PageException { 241 if(source instanceof HTTPResource) { 242 HTTPResource httpSource = (HTTPResource)source; 243 if(!StringUtil.isEmpty(proxyServer,true)){ 244 ProxyData data=new ProxyDataImpl(proxyServer,proxyPort,proxyUser,proxyPassword); 245 httpSource.setProxyData(data); 246 } 247 if(!StringUtil.isEmpty(userAgent)) 248 httpSource.setUserAgent(userAgent); 249 if(timeout>-1)httpSource.setTimeout(timeout*1000); 250 } 251 252 try { 253 if(ACTION_CREATE==action) doActionCreate(); 254 else if(ACTION_READ==action) doActionRead(); 255 } 256 catch(Exception e) { 257 throw Caster.toPageException(e); 258 } 259 return SKIP_BODY; 260 } 261 262 private void doActionCreate() throws PageException { 263 264 // name 265 Query qry; 266 Struct props; 267 boolean splitString=true; 268 if(name!=null) { 269 Struct data; 270 if(name instanceof String){ 271 data=Caster.toStruct(pageContext.getVariable(Caster.toString(name))); 272 } 273 else data = Caster.toStruct(name,false); 274 275 qry = FeedQuery.toQuery(data,false); 276 props = FeedProperties.toProperties(data); 277 splitString=false; 278 } 279 else if(query!=null && properties!=null) { 280 qry = FeedQuery.toQuery(Caster.toQuery(query)); 281 props = FeedProperties.toProperties(Caster.toStruct(properties,false)); 282 } 283 else { 284 throw new ApplicationException("missing attribute [name] or attributes [query] and [properties]"); 285 } 286 287 StringBuffer xml=new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); 288 289 if(type==TYPE_AUTO){ 290 String version=Caster.toString(props.get("version","rss"),"rss"); 291 type=StringUtil.startsWithIgnoreCase(version, "rss")?TYPE_RSS:TYPE_ATOM; 292 } 293 if(type==TYPE_RSS) { 294 createRSS(xml,qry,props,splitString); 295 } 296 else { 297 createAtom(xml, qry, props, splitString); 298 } 299 300 // variable 301 if(!StringUtil.isEmpty(xmlVar)) { 302 pageContext.setVariable(xmlVar, xml); 303 } 304 //file 305 if(outputFile!=null){ 306 if(outputFile.exists() && !overwrite) 307 throw new ApplicationException("destiniation file ["+outputFile+"] already exist"); 308 309 310 311 if(StringUtil.isEmpty(charset)) charset=pageContext.getConfig().getResourceCharset(); 312 313 try { 314 IOUtil.write(outputFile, xml.toString(),charset,false); 315 } catch (IOException e) { 316 throw Caster.toPageException(e); 317 } 318 } 319 320 321 322 323 324 /* 325 <cffeed 326 action = "create" 327 name = "#structure#" 328 One or both of the following: 329 outputFile = "path" 330 xmlVar = "variable name" 331 optional 332 overwrite = "no|yes"> 333 334 <cffeed 335 action = "create" 336 properties = "#metadata structure#" 337 query = "#items/entries query name#" 338 One or both of the following: 339 outputFile = "path" 340 xmlVar = "variable name" 341 optional 342 columnMap = "mapping structure" 343 overwrite = "no|yes"> 344 */ 345 346 } 347 348 private void createAtom(StringBuffer xml, Query query, Struct props,boolean splitString) throws PageException { 349 int rows=query.getRowCount(); 350 351 append(xml,0,"<feed xmlns=\"http://www.w3.org/2005/Atom\">"); 352 353 propTag(props,xml,1,new String[]{"title"},"title", 354 new String[][]{ 355 new String[]{"type","type"} 356 } 357 ); 358 propTag(props,xml,1,new String[]{"subtitle"},"subtitle", 359 new String[][]{ 360 new String[]{"type","type"} 361 } 362 ); 363 propTag(props,xml,1,new String[]{"updated"},"updated",null); 364 propTag(props,xml,1,new String[]{"id"},"id",null); 365 propTag(props,xml,1,new String[]{"link"},"link", 366 new String[][]{ 367 new String[]{"rel","rel"}, 368 new String[]{"type","type"}, 369 new String[]{"hreflang","hreflang"}, 370 new String[]{"href","href"} 371 } 372 ); 373 propTag(props,xml,1,new String[]{"rights"},"rights",null); 374 propTag(props,xml,1,new String[]{"generator"},"generator", 375 new String[][]{ 376 new String[]{"uri","uri"}, 377 new String[]{"version","version"} 378 } 379 ); 380 381 // items 382 for(int row=1;row<=rows;row++) { 383 append(xml,1,"<entry>"); 384 385 tag(xml,2, 386 new Pair("title",query.getAt(FeedQuery.TITLE, row, null)), 387 new Pair[]{ 388 new Pair("type",query.getAt(FeedQuery.TITLETYPE, row, null)) 389 },false,splitString 390 ); 391 tag(xml,2, 392 new Pair("link",null), 393 new Pair[]{ 394 new Pair("href",query.getAt(FeedQuery.LINKHREF, row, null)), 395 new Pair("hreflang",query.getAt(FeedQuery.LINKHREFLANG, row, null)), 396 new Pair("length",query.getAt(FeedQuery.LINKLENGTH, row, null)), 397 new Pair("rel",query.getAt(FeedQuery.LINKREL, row, null)), 398 new Pair("title",query.getAt(FeedQuery.LINKTITLE, row, null)), 399 new Pair("type",query.getAt(FeedQuery.LINKTYPE, row, null)) 400 },false,splitString 401 ); 402 tag(xml,2,new Pair("id",query.getAt(FeedQuery.ID, row, null)),null,true,false); 403 tag(xml,2,new Pair("updated",query.getAt(FeedQuery.UPDATEDDATE, row, null)),null,true,false); 404 tag(xml,2,new Pair("published",query.getAt(FeedQuery.PUBLISHEDDATE, row, null)),null,true,false); 405 tag(xml,2, 406 new Pair("author",null), 407 new Pair[]{ 408 new Pair("email",query.getAt(FeedQuery.AUTHOREMAIL, row, null)), 409 new Pair("name",query.getAt(FeedQuery.AUTHORNAME, row, null)), 410 new Pair("uri",query.getAt(FeedQuery.AUTHORURI, row, null)) 411 },false,splitString 412 ); 413 tag(xml,2, 414 new Pair("category",null), 415 new Pair[]{ 416 new Pair("label",query.getAt(FeedQuery.CATEGORYLABEL, row, null)), 417 new Pair("scheme",query.getAt(FeedQuery.CATEGORYSCHEME, row, null)), 418 new Pair("term",query.getAt(FeedQuery.CATEGORYTERM, row, null)), 419 },false,splitString 420 ); 421 tag(xml,2, 422 new Pair("contributor",null), 423 new Pair[]{ 424 new Pair("email",query.getAt(FeedQuery.CONTRIBUTOREMAIL, row, null)), 425 new Pair("name",query.getAt(FeedQuery.CONTRIBUTORNAME, row, null)), 426 new Pair("uri",query.getAt(FeedQuery.CONTRIBUTORURI, row, null)) 427 },false,splitString 428 ); 429 tag(xml,2, 430 new Pair("content",query.getAt(FeedQuery.CONTENT, row, null)), 431 new Pair[]{ 432 new Pair("src",query.getAt(FeedQuery.CONTENTSRC, row, null)), 433 new Pair("type",query.getAt(FeedQuery.CONTENTTYPE, row, null)), 434 new Pair("type",query.getAt(FeedQuery.CONTENTTYPE, row, null)) 435 },false,splitString 436 ); 437 tag(xml,2,new Pair("rights",query.getAt(FeedQuery.RIGHTS, row, null)),null,true,false); 438 tag(xml,2, 439 new Pair("summary",query.getAt(FeedQuery.SUMMARY, row, null)), 440 new Pair[]{ 441 new Pair("type",query.getAt(FeedQuery.SUMMARYTYPE, row, null)) 442 },false,splitString 443 ); 444 445 append(xml,1,"</entry>"); 446 } 447 448 449 450 451 append(xml,0,"</feed>"); 452 453 } 454 private void createRSS(StringBuffer xml, Query query, Struct props, boolean splitString) throws PageException { 455 int rows=query.getRowCount(); 456 457 append(xml,0,"<rss xmlns:taxo=\"http://purl.org/rss/1.0/modules/taxonomy/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" version=\"2.0\">"); 458 append(xml,1,"<channel>"); 459 460 // title 461 propTag(props,xml,2,new String[]{"title"},"title",null); 462 propTag(props,xml,2,new String[]{"link"},"link",null); 463 propTag(props,xml,2,new String[]{"description","subtitle"},"description",null); 464 propTag(props,xml,2,new String[]{"language"},"language",null); 465 propTag(props,xml,2,new String[]{"copyright"},"copyright",null); 466 propTag(props,xml,2,new String[]{"managingEditor"},"managingEditor",null); 467 propTag(props,xml,2,new String[]{"webMaster"},"webMaster",null); 468 propTag(props,xml,2,new String[]{"pubDate"},"pubDate",null); 469 propTag(props,xml,2,new String[]{"lastBuildDate"},"lastBuildDate",null); 470 propTag(props,xml,2,new String[]{"category"},"category", 471 new String[][]{ 472 new String[]{"domain","domain"} 473 } 474 ); 475 propTag(props,xml,2,new String[]{"generator"},"generator",null); 476 propTag(props,xml,2,new String[]{"docs"},"docs",null); 477 propTag(props,xml,2,new String[]{"cloud"},"cloud", 478 new String[][]{ 479 new String[]{"domain","domain"}, 480 new String[]{"port","port"}, 481 new String[]{"path","path"}, 482 new String[]{"registerProcedure","registerProcedure"}, 483 new String[]{"protocol","protocol"} 484 } 485 ); 486 propTag(props,xml,2,new String[]{"ttl"},"ttl",null); 487 propTag(props,xml,2,new String[]{"image"},"image", 488 new String[][]{ 489 new String[]{"url","url"}, 490 new String[]{"title","title"}, 491 new String[]{"link","link"}, 492 new String[]{"width","width"}, 493 new String[]{"height","height"}, 494 new String[]{"description","description"} 495 } 496 ,true); 497 propTag(props,xml,2,new String[]{"textInput"},"textInput", 498 new String[][]{ 499 new String[]{"title","title"}, 500 new String[]{"description","description"}, 501 new String[]{"name","name"}, 502 new String[]{"link","link"} 503 } 504 ,true); 505 propTag(props,xml,2,new String[]{"skipHours"},"skipHours",null); 506 propTag(props,xml,2,new String[]{"skipDays"},"skipDays",null); 507 508 // items 509 for(int row=1;row<=rows;row++) { 510 append(xml,2,"<item>"); 511 512 tag(xml,3,new Pair("title",(query.getAt(FeedQuery.TITLE, row, null))),null,true,false); 513 tag(xml,3,new Pair("description",(query.getAt(FeedQuery.CONTENT, row, null))),null,true,false); 514 tag(xml,3,new Pair("link",query.getAt(FeedQuery.RSSLINK, row, null)),null,false,false); 515 tag(xml,3,new Pair("author",query.getAt(FeedQuery.AUTHOREMAIL, row, null)),null,false,false); 516 tag(xml,3, 517 new Pair("category",query.getAt(FeedQuery.CATEGORYLABEL, row, null)), 518 new Pair[]{ 519 new Pair("domain",query.getAt(FeedQuery.CATEGORYSCHEME, row, null)) 520 },false,splitString 521 ); 522 tag(xml,3,new Pair("comments",query.getAt(FeedQuery.COMMENTS, row, null)),null,false,false); 523 tag(xml,3, 524 new Pair("enclosure",null), 525 new Pair[]{ 526 new Pair("url",query.getAt(FeedQuery.LINKHREF, row, null)), 527 new Pair("length",query.getAt(FeedQuery.LINKLENGTH, row, null)), 528 new Pair("type",query.getAt(FeedQuery.LINKTYPE, row, null)) 529 },false,splitString 530 ); 531 tag(xml,3, 532 new Pair("guid",query.getAt(FeedQuery.ID, row, null)), 533 new Pair[]{ 534 new Pair("isPermaLink",query.getAt(FeedQuery.IDPERMALINK, row, null)) 535 },false,splitString 536 ); 537 tag(xml,3,new Pair("pubDate",query.getAt(FeedQuery.PUBLISHEDDATE, row, null)),null,false,splitString); 538 tag(xml,3, 539 new Pair("source",query.getAt(FeedQuery.SOURCE, row, null)), 540 new Pair[]{ 541 new Pair("url",query.getAt(FeedQuery.SOURCEURL, row, null)) 542 },false,false 543 ); 544 545 546 547 548 549 append(xml,2,"</item>"); 550 } 551 552 append(xml,1,"</channel>"); 553 append(xml,0,"</rss>"); 554 555 } 556 557 private void propTag(Struct props,StringBuffer xml, int count,String[] srcNames,String trgName,String[][] attrNames) throws PageException { 558 propTag(props, xml, count, srcNames, trgName, attrNames,false); 559 } 560 561 562 private void propTag(Struct props,StringBuffer xml, int count,String[] srcNames,String trgName,String[][] attrNames, boolean childrenAsTag) throws PageException { 563 Object value; 564 for(int i=0;i<srcNames.length;i++) { 565 value=props.get(srcNames[i], null); 566 567 if(value instanceof Array){ 568 Array arr = (Array)value; 569 int size = arr.size(); 570 for(int y=1;y<=size;y++) { 571 propTag(xml, count, arr.get(y, null), trgName, attrNames, childrenAsTag); 572 } 573 break; 574 } 575 if(value !=null) { 576 propTag(xml, count, value, trgName, attrNames, childrenAsTag); 577 break; 578 } 579 } 580 } 581 582 private boolean propTag(StringBuffer xml, int count,Object value,String trgName,String[][] attrNames, boolean childrenAsTag) throws PageException { 583 if(!StringUtil.isEmpty(value)){ 584 Pair[] attrs; 585 if(value instanceof Struct && attrNames!=null) { 586 Struct sct = (Struct)value; 587 Object attrValue; 588 ArrayList al=new ArrayList(); 589 for(int i=0;i<attrNames.length;i++) { 590 attrValue=sct.get(attrNames[i][0], null); 591 if(attrValue!=null) { 592 al.add(new Pair(attrNames[i][1],attrValue)); 593 } 594 } 595 attrs=(Pair[]) al.toArray(new Pair[al.size()]); 596 } 597 else attrs=null; 598 tag(xml,count,new Pair(trgName,FeedQuery.getValue(value)),attrs,false,false,childrenAsTag); 599 return true; 600 } 601 return false; 602 603 } 604 private void tag(StringBuffer xml, int count, Pair tag, Pair[] attrs,boolean required,boolean splitString) throws PageException { 605 tag(xml, count, tag, attrs, required, splitString,false); 606 } 607 608 private void tag(StringBuffer xml, int count, Pair tag, Pair[] attrs,boolean required,boolean splitString, boolean childrenAsTag) throws PageException { 609 if(!required && StringUtil.isEmpty(tag.getValue())){ 610 if(attrs==null || attrs.length==0)return; 611 int c=0; 612 for(int i=0;i<attrs.length;i++){ 613 if(!StringUtil.isEmpty(attrs[i].getValue())) c++; 614 615 } 616 if(c==0)return; 617 } 618 619 if(tag.getValue() instanceof Array){ 620 Array arr=(Array) tag.getValue(); 621 int len=arr.size(); 622 for(int i=1;i<=len;i++) { 623 _tag(xml, tag.getName(),arr.get(i, null), attrs, count, i,false,childrenAsTag); 624 } 625 return; 626 627 } 628 if(splitString && tag.getValue() instanceof String) { 629 String strValue=(String) tag.getValue(); 630 Array arr = List.listToArray(strValue, ','); 631 if(arr.size()>1){ 632 int len=arr.size(); 633 for(int i=1;i<=len;i++) { 634 _tag(xml, tag.getName(),arr.get(i, null), attrs, count, i,true,childrenAsTag); 635 } 636 return; 637 } 638 } 639 _tag(xml, tag.getName(), tag.getValue(), attrs, count, 0,false,childrenAsTag); 640 641 642 } 643 644 private void _tag(StringBuffer xml, String tagName,Object tagValue, Pair[] attrs,int count, int index, boolean splitString, boolean childrenAsTag) throws PageException { 645 for(int i=0;i<count;i++)xml.append("\t"); 646 xml.append('<'); 647 xml.append(tagName); 648 649 Object attrValue; 650 if(attrs!=null && !childrenAsTag){ 651 for(int i=0;i<attrs.length;i++){ 652 attrValue=attrs[i].getValue(); 653 if(index>0){ 654 if(attrValue instanceof Array)attrValue=((Array)attrValue).get(index,null); 655 else if(splitString && attrValue instanceof String){ 656 Array arr = List.listToArray((String)attrValue, ','); 657 attrValue=arr.get(index,null); 658 } 659 } 660 if(StringUtil.isEmpty(attrValue)) continue; 661 662 xml.append(' '); 663 xml.append(attrs[i].getName()); 664 xml.append("=\""); 665 xml.append(XMLUtil.escapeXMLString(toString(attrValue))); 666 xml.append("\""); 667 } 668 } 669 670 xml.append('>'); 671 xml.append(toString(tagValue)); 672 if(attrs!=null && attrs.length>0 && childrenAsTag){ 673 xml.append('\n'); 674 for(int i=0;i<attrs.length;i++){ 675 attrValue=attrs[i].getValue(); 676 if(index>0){ 677 if(attrValue instanceof Array)attrValue=((Array)attrValue).get(index,null); 678 else if(splitString && attrValue instanceof String){ 679 Array arr = List.listToArray((String)attrValue, ','); 680 attrValue=arr.get(index,null); 681 } 682 } 683 if(StringUtil.isEmpty(attrValue)) continue; 684 685 for(int y=0;y<count+1;y++)xml.append("\t"); 686 xml.append('<'); 687 xml.append(attrs[i].getName()); 688 xml.append('>'); 689 //xml.append(XMLUtil.escapeXMLString(toString(attrValue))); 690 xml.append(toString(attrValue)); 691 xml.append("</"); 692 xml.append(attrs[i].getName()); 693 xml.append(">\n"); 694 } 695 for(int y=0;y<count;y++)xml.append("\t"); 696 } 697 698 699 700 701 xml.append("</"); 702 xml.append(tagName); 703 xml.append(">\n"); 704 705 } 706 707 708 private String toString(Object value) throws PageException { 709 if(Decision.isDateAdvanced(value, false)) return GetHttpTimeString.invoke(Caster.toDatetime(value, pageContext.getTimeZone())); 710 return XMLUtil.escapeXMLString(Caster.toString(value)); 711 } 712 713 private static void append(StringBuffer xml, int count, String value) { 714 for(int i=0;i<count;i++)xml.append("\t"); 715 xml.append(value); 716 xml.append("\n"); 717 } 718 719 private void doActionRead() throws IOException, SAXException, PageException { 720 required("Feed", "read", "source", source); 721 722 if(outputFile!=null && outputFile.exists() && !overwrite) 723 throw new ApplicationException("outputFile file ["+outputFile+"] already exists"); 724 725 726 String charset=null; 727 728 // plain output 729 // xmlVar 730 if(outputFile!=null) { 731 IOUtil.copy(source, outputFile); 732 } 733 // outputFile 734 String strFeed=null; 735 if(!StringUtil.isEmpty(xmlVar)) { 736 strFeed = IOUtil.toString(outputFile!=null?outputFile:source,charset); 737 pageContext.setVariable(xmlVar, strFeed); 738 } 739 // Input Source 740 InputSource is=null; 741 Reader r=null; 742 if(strFeed!=null)is=new InputSource(new StringReader(strFeed)); 743 else if(outputFile!=null)is=new InputSource(r=IOUtil.getReader(outputFile, charset)); 744 else is=new InputSource(r=IOUtil.getReader(source, charset)); 745 is.setSystemId(source.getPath()); 746 747 try{ 748 FeedHandler feed=new FeedHandler(source); 749 Struct data = feed.getData(); 750 //print.e(data.keys()); 751 //print.e(data); 752 // properties 753 if(properties!=null) { 754 String strProp = Caster.toString(properties,null); 755 if(strProp==null)throw new ApplicationException("attribute [properties] should be of type string"); 756 pageContext.setVariable(strProp, FeedProperties.toProperties(data)); 757 } 758 759 // query or enclosure 760 railo.runtime.type.Query qry=null; 761 if(query!=null || enclosureDir!=null){ 762 qry=FeedQuery.toQuery(data,feed.hasDC()); 763 } 764 765 // query 766 if(query!=null) { 767 String strQuery = Caster.toString(query,null); 768 if(strQuery==null)throw new ApplicationException("attribute [query] should be of type string"); 769 pageContext.setVariable(strQuery, qry); 770 } 771 // enclosure 772 773 if(enclosureDir!=null) { 774 int rows=qry.getRowCount(); 775 String strUrl=null; 776 Resource src,dest; 777 for(int row=1;row<=rows;row++) { 778 strUrl=Caster.toString(qry.getAt(FeedQuery.LINKHREF, row, null),null); 779 if(!StringUtil.isEmpty(strUrl)){ 780 src=ResourceUtil.toResourceNotExisting(pageContext, strUrl); 781 dest=enclosureDir.getRealResource(src.getName()); 782 if(!ignoreEnclosureError && !overwriteEnclosure && dest.exists()) 783 throw new ApplicationException("enclosure file ["+dest+"] already exists"); 784 try { 785 IOUtil.copy(src, dest); 786 } 787 catch(IOException ioe) { 788 if(!ignoreEnclosureError)throw ioe; 789 } 790 } 791 } 792 } 793 794 // name 795 if(name!=null) { 796 String strName = Caster.toString(name,null); 797 if(strName==null)throw new ApplicationException("attribute [name] should be of type string"); 798 pageContext.setVariable(strName, data); 799 } 800 801 802 803 } 804 finally { 805 IOUtil.closeEL(r); 806 } 807 } 808 }