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