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.converter; 020 021import java.io.IOException; 022import java.io.Serializable; 023import java.io.Writer; 024import java.util.Date; 025import java.util.HashSet; 026import java.util.Iterator; 027import java.util.List; 028import java.util.ListIterator; 029import java.util.Map; 030import java.util.Map.Entry; 031import java.util.Set; 032 033import lucee.commons.lang.StringUtil; 034import lucee.runtime.Component; 035import lucee.runtime.ComponentScope; 036import lucee.runtime.ComponentSpecificAccess; 037import lucee.runtime.PageContext; 038import lucee.runtime.component.Property; 039import lucee.runtime.exp.PageException; 040import lucee.runtime.functions.displayFormatting.DateFormat; 041import lucee.runtime.functions.displayFormatting.TimeFormat; 042import lucee.runtime.op.Caster; 043import lucee.runtime.orm.ORMUtil; 044import lucee.runtime.text.xml.XMLCaster; 045import lucee.runtime.type.Array; 046import lucee.runtime.type.Collection; 047import lucee.runtime.type.Collection.Key; 048import lucee.runtime.type.KeyImpl; 049import lucee.runtime.type.ObjectWrap; 050import lucee.runtime.type.Query; 051import lucee.runtime.type.Struct; 052import lucee.runtime.type.UDF; 053import lucee.runtime.type.dt.DateTime; 054import lucee.runtime.type.dt.DateTimeImpl; 055import lucee.runtime.type.dt.TimeSpan; 056import lucee.runtime.type.util.ComponentProUtil; 057import lucee.runtime.type.util.ComponentUtil; 058import lucee.runtime.type.util.KeyConstants; 059 060import org.w3c.dom.Node; 061 062/** 063 * class to serialize and desirilize WDDX Packes 064 */ 065public final class ScriptConverter extends ConverterSupport { 066 private static final Collection.Key REMOTING_FETCH = KeyImpl.intern("remotingFetch"); 067 068 private int deep=1; 069 private boolean ignoreRemotingFetch=true; 070 071 /** 072 * constructor of the class 073 */ 074 public ScriptConverter() { 075 } 076 public ScriptConverter(boolean ignoreRemotingFetch) { 077 this.ignoreRemotingFetch=ignoreRemotingFetch; 078 } 079 080 081 /** 082 * serialize Serializable class 083 * @param serializable 084 * @param sb 085 * @throws ConverterException 086 */ 087 private void _serializeSerializable(Serializable serializable, StringBuilder sb) throws ConverterException { 088 089 sb.append(goIn()); 090 sb.append("evaluateJava('"); 091 try { 092 sb.append(JavaConverter.serialize(serializable)); 093 } catch (IOException e) { 094 throw toConverterException(e); 095 } 096 sb.append("')"); 097 098 } 099 100 /** 101 * serialize a Date 102 * @param date Date to serialize 103 * @param sb 104 * @throws ConverterException 105 */ 106 private void _serializeDate(Date date, StringBuilder sb) throws ConverterException { 107 _serializeDateTime(new DateTimeImpl(date),sb); 108 } 109 /** 110 * serialize a DateTime 111 * @param dateTime DateTime to serialize 112 * @param sb 113 * @throws ConverterException 114 */ 115 private void _serializeDateTime(DateTime dateTime, StringBuilder sb) throws ConverterException { 116 117 118 try { 119 sb.append(goIn()); 120 sb.append("createDateTime("); 121 sb.append(DateFormat.call(null,dateTime,"yyyy,m,d")); 122 sb.append(','); 123 sb.append(TimeFormat.call(null,dateTime,"H,m,s,l,\"z\"")); 124 sb.append(')'); 125 } 126 catch (PageException e) { 127 throw toConverterException(e); 128 } 129 } 130 131 /** 132 * serialize a Array 133 * @param array Array to serialize 134 * @param sb 135 * @param done 136 * @throws ConverterException 137 */ 138 private void _serializeArray(Array array, StringBuilder sb, Set<Object> done) throws ConverterException { 139 _serializeList(array.toList(),sb,done); 140 } 141 142 /** 143 * serialize a List (as Array) 144 * @param list List to serialize 145 * @param sb 146 * @param done 147 * @throws ConverterException 148 */ 149 private void _serializeList(List list, StringBuilder sb, Set<Object> done) throws ConverterException { 150 151 sb.append(goIn()); 152 sb.append("["); 153 boolean doIt=false; 154 ListIterator it=list.listIterator(); 155 while(it.hasNext()) { 156 if(doIt)sb.append(','); 157 doIt=true; 158 _serialize(it.next(),sb,done); 159 } 160 161 sb.append(']'); 162 } 163 164 /** 165 * serialize a Struct 166 * @param struct Struct to serialize 167 * @param sb 168 * @param done 169 * @throws ConverterException 170 */ 171 public void _serializeStruct(Struct struct, StringBuilder sb, Set<Object> done) throws ConverterException { 172 sb.append(goIn()); 173 sb.append('{'); 174 Iterator it=struct.keyIterator(); 175 boolean doIt=false; 176 deep++; 177 while(it.hasNext()) { 178 String key=Caster.toString(it.next(),""); 179 if(doIt)sb.append(','); 180 doIt=true; 181 sb.append('\''); 182 sb.append(escape(key)); 183 sb.append('\''); 184 sb.append(':'); 185 _serialize(struct.get(key,null),sb,done); 186 } 187 deep--; 188 189 sb.append('}'); 190 } 191 192 public String serializeStruct(Struct struct, Set<Collection.Key> ignoreSet) throws ConverterException { 193 StringBuilder sb =new StringBuilder(); 194 sb.append(goIn()); 195 sb.append("{"); 196 boolean hasIgnores=ignoreSet!=null; 197 Iterator<Key> it = struct.keyIterator(); 198 boolean doIt=false; 199 deep++; 200 Key key; 201 while(it.hasNext()) { 202 key = it.next(); 203 if(hasIgnores && ignoreSet.contains(key)) continue; 204 if(doIt)sb.append(','); 205 doIt=true; 206 sb.append('\''); 207 sb.append(escape(key.getString())); 208 sb.append('\''); 209 sb.append(':'); 210 _serialize(struct.get(key,null),sb,new HashSet<Object>()); 211 } 212 deep--; 213 214 return sb.append('}').toString(); 215 } 216 217 /** 218 * serialize a Map (as Struct) 219 * @param map Map to serialize 220 * @param sb 221 * @param done 222 * @throws ConverterException 223 */ 224 private void _serializeMap(Map map, StringBuilder sb, Set<Object> done) throws ConverterException { 225 if(map instanceof Serializable) { 226 _serializeSerializable((Serializable)map,sb); 227 return; 228 } 229 sb.append(goIn()); 230 sb.append("{"); 231 232 Iterator it=map.keySet().iterator(); 233 boolean doIt=false; 234 deep++; 235 while(it.hasNext()) { 236 Object key=it.next(); 237 if(doIt)sb.append(','); 238 doIt=true; 239 sb.append('\''); 240 sb.append(escape(key.toString())); 241 sb.append('\''); 242 sb.append(':'); 243 _serialize(map.get(key),sb,done); 244 } 245 deep--; 246 247 sb.append('}'); 248 } 249 /** 250 * serialize a Component 251 * @param component Component to serialize 252 * @param sb 253 * @param done 254 * @throws ConverterException 255 */ 256 private void _serializeComponent(Component c, StringBuilder sb, Set<Object> done) throws ConverterException { 257 258 ComponentSpecificAccess cw = new ComponentSpecificAccess(Component.ACCESS_PRIVATE,c); 259 260 sb.append(goIn()); 261 try { 262 sb.append("evaluateComponent('"+c.getAbsName()+"','"+ComponentUtil.md5(c)+"',{"); 263 } catch (Exception e) { 264 throw toConverterException(e); 265 } 266 267 boolean doIt=false; 268 Object member; 269 { 270 271 Iterator<Entry<Key, Object>> it = cw.entryIterator(); 272 deep++; 273 Entry<Key, Object> e; 274 while(it.hasNext()) { 275 e = it.next(); 276 member = e.getValue(); 277 if(member instanceof UDF)continue; 278 if(doIt)sb.append(','); 279 doIt=true; 280 sb.append('\''); 281 sb.append(escape(e.getKey().getString())); 282 sb.append('\''); 283 sb.append(':'); 284 _serialize(member,sb,done); 285 } 286 sb.append("}"); 287 deep--; 288 } 289 { 290 boolean isPeristent = ComponentProUtil.isPersistent(c); 291 292 ComponentScope scope = c.getComponentScope(); 293 Iterator<Entry<Key, Object>> it = scope.entryIterator(); 294 sb.append(",{"); 295 deep++; 296 doIt=false; 297 Property p; 298 Boolean remotingFetch; 299 Struct props = ignoreRemotingFetch?null:ComponentUtil.getPropertiesAsStruct(c,false); 300 Entry<Key, Object> e; 301 Key k; 302 while(it.hasNext()) { 303 e = it.next(); 304 k = e.getKey(); 305 //String key=Caster.toString(it.next(),""); 306 if(KeyConstants._THIS.equalsIgnoreCase(k))continue; 307 if(!ignoreRemotingFetch) { 308 p=(Property) props.get(k,null); 309 if(p!=null) { 310 remotingFetch=Caster.toBoolean(p.getDynamicAttributes().get(REMOTING_FETCH,null),null); 311 if(remotingFetch==null){ 312 if(isPeristent && ORMUtil.isRelated(p)) continue; 313 } 314 else if(!remotingFetch.booleanValue()) continue; 315 } 316 } 317 318 319 320 member = e.getValue(); 321 if(member instanceof UDF)continue; 322 if(doIt)sb.append(','); 323 doIt=true; 324 sb.append('\''); 325 sb.append(escape(k.getString())); 326 sb.append('\''); 327 sb.append(':'); 328 _serialize(member,sb,done); 329 } 330 sb.append("}"); 331 deep--; 332 } 333 334 sb.append(")"); 335 //sb.append(""); 336 //throw new ConverterException("can't serialize a component "+component.getDisplayName()); 337 } 338 339 /** 340 * serialize a Query 341 * @param query Query to serialize 342 * @param sb 343 * @param done 344 * @throws ConverterException 345 */ 346 private void _serializeQuery(Query query, StringBuilder sb, Set<Object> done) throws ConverterException { 347 348 //Collection.Key[] keys = query.keys(); 349 Iterator<Key> it = query.keyIterator(); 350 Key k; 351 sb.append(goIn()); 352 sb.append("query("); 353 354 355 deep++; 356 boolean oDoIt=false; 357 int len=query.getRecordcount(); 358 while(it.hasNext()) { 359 k = it.next(); 360 if(oDoIt)sb.append(','); 361 oDoIt=true; 362 sb.append(goIn()); 363 sb.append('\''); 364 sb.append(escape(k.getString())); 365 sb.append('\''); 366 sb.append(":["); 367 boolean doIt=false; 368 for(int y=1;y<=len;y++) { 369 if(doIt)sb.append(','); 370 doIt=true; 371 try { 372 _serialize(query.getAt(k,y),sb,done); 373 } catch (PageException e) { 374 _serialize(e.getMessage(),sb,done); 375 } 376 } 377 sb.append(']'); 378 } 379 deep--; 380 381 sb.append(')'); 382 383 } 384 385 /** 386 * serialize a Object to his xml Format represenation 387 * @param object Object to serialize 388 * @param sb StringBuilder to write data 389 * @param done 390 * @throws ConverterException 391 */ 392 private void _serialize(Object object, StringBuilder sb, Set<Object> done) throws ConverterException { 393 //try { 394 deep++; 395 // NULL 396 if(object==null) { 397 sb.append(goIn()); 398 sb.append("nullValue()"); 399 deep--; 400 return; 401 } 402 // String 403 if(object instanceof String) { 404 sb.append(goIn()); 405 sb.append("'"); 406 sb.append(escape(object.toString())); 407 sb.append("'"); 408 deep--; 409 return; 410 } 411 // Number 412 if(object instanceof Number) { 413 sb.append(goIn()); 414 sb.append(Caster.toString(((Number)object))); 415 deep--; 416 return; 417 } 418 // Boolean 419 if(object instanceof Boolean) { 420 sb.append(goIn()); 421 sb.append(Caster.toString(((Boolean)object).booleanValue())); 422 deep--; 423 return; 424 } 425 // DateTime 426 if(object instanceof DateTime) { 427 _serializeDateTime((DateTime)object,sb); 428 deep--; 429 return; 430 } 431 // Date 432 if(object instanceof Date) { 433 _serializeDate((Date)object,sb); 434 deep--; 435 return; 436 } 437 // XML 438 if(object instanceof Node) { 439 _serializeXML((Node)object,sb); 440 deep--; 441 return; 442 } 443 if(object instanceof ObjectWrap) { 444 try { 445 _serialize(((ObjectWrap)object).getEmbededObject(), sb,done); 446 } catch (PageException e) { 447 throw toConverterException(e); 448 } 449 deep--; 450 return; 451 } 452 // Timespan 453 if(object instanceof TimeSpan) { 454 _serializeTimeSpan((TimeSpan) object,sb); 455 deep--; 456 return; 457 } 458 Object raw = LazyConverter.toRaw(object); 459 if(done.contains(raw)) { 460 sb.append(goIn()); 461 sb.append("nullValue()"); 462 deep--; 463 return; 464 } 465 466 done.add(raw); 467 try { 468 // Component 469 if(object instanceof Component) { 470 _serializeComponent((Component)object,sb,done); 471 deep--; 472 return; 473 } 474 475 // Struct 476 if(object instanceof Struct) { 477 _serializeStruct((Struct)object,sb,done); 478 deep--; 479 return; 480 } 481 // Map 482 if(object instanceof Map) { 483 _serializeMap((Map)object,sb,done); 484 deep--; 485 return; 486 } 487 // Array 488 if(object instanceof Array) { 489 _serializeArray((Array)object,sb,done); 490 deep--; 491 return; 492 } 493 // List 494 if(object instanceof List) { 495 _serializeList((List)object,sb,done); 496 deep--; 497 return; 498 } 499 // Query 500 if(object instanceof Query) { 501 _serializeQuery((Query)object,sb,done); 502 deep--; 503 return; 504 } 505 // String Converter 506 if(object instanceof ScriptConvertable) { 507 sb.append(((ScriptConvertable)object).serialize()); 508 deep--; 509 return; 510 } 511 if(object instanceof Serializable) { 512 _serializeSerializable((Serializable)object,sb); 513 deep--; 514 return; 515 } 516 } 517 finally { 518 done.remove(raw); 519 } 520 throw new ConverterException("can't serialize Object of type [ "+Caster.toClassName(object)+" ]"); 521 //deep--; 522 /*} 523 catch(StackOverflowError soe){ 524 throw soe; 525 }*/ 526 } 527 528 529 530 private void _serializeXML(Node node, StringBuilder sb) { 531 node=XMLCaster.toRawNode(node); 532 sb.append(goIn()); 533 sb.append("xmlParse('"); 534 sb.append(escape(XMLCaster.toString(node,""))); 535 sb.append("')"); 536 537 } 538 539 private void _serializeTimeSpan(TimeSpan span, StringBuilder sb) { 540 541 sb.append(goIn()); 542 sb.append("createTimeSpan("); 543 sb.append(span.getDay()); 544 sb.append(','); 545 sb.append(span.getHour()); 546 sb.append(','); 547 sb.append(span.getMinute()); 548 sb.append(','); 549 sb.append(span.getSecond()); 550 sb.append(')'); 551 552 } 553 554 555 private String escape(String str) { 556 return StringUtil.replace(StringUtil.replace(str,"'","''",false),"#","##",false); 557 } 558 559 @Override 560 public void writeOut(PageContext pc, Object source, Writer writer) throws ConverterException, IOException { 561 writer.write(serialize(source)); 562 writer.flush(); 563 } 564 565 /** 566 * serialize a Object to his literal Format 567 * @param object Object to serialize 568 * @return serialized wddx package 569 * @throws ConverterException 570 */ 571 public String serialize(Object object) throws ConverterException { 572 deep=0; 573 StringBuilder sb=new StringBuilder(); 574 _serialize(object,sb,new HashSet<Object>()); 575 return sb.toString(); 576 } 577 578 579 /** 580 * @return return current blockquote 581 */ 582 private String goIn() { 583 /*StringBuilder rtn=new StringBuilder('\n'); 584 for(int i=0;i<deep;i++) rtn.append('\t'); 585 return rtn.toString(); 586 /*/ 587 588 return ""; 589 } 590 591 592}