001 package railo.transformer.library.tag; 002 003 import java.io.IOException; 004 import java.io.InputStream; 005 import java.io.Reader; 006 import java.net.URISyntaxException; 007 import java.util.ArrayList; 008 import java.util.Iterator; 009 import java.util.Map; 010 import java.util.Set; 011 012 import org.xml.sax.Attributes; 013 import org.xml.sax.InputSource; 014 import org.xml.sax.SAXException; 015 import org.xml.sax.XMLReader; 016 import org.xml.sax.helpers.DefaultHandler; 017 018 import railo.commons.collections.HashTable; 019 import railo.commons.io.IOUtil; 020 import railo.commons.io.SystemUtil; 021 import railo.commons.io.res.Resource; 022 import railo.commons.io.res.filter.ExtensionResourceFilter; 023 import railo.commons.io.res.util.ResourceUtil; 024 import railo.runtime.op.Caster; 025 import railo.runtime.text.xml.XMLUtil; 026 import railo.runtime.type.util.ArrayUtil; 027 028 /** 029 * Die Klasse TagLibFactory liest die XML Repraesentation einer TLD ein 030 * und laedt diese in eine Objektstruktur. 031 * Sie tut dieses mithilfe eines Sax Parser. 032 * Die Klasse kann sowohl einzelne Files oder gar ganze Verzeichnisse von TLD laden. 033 */ 034 public final class TagLibFactory extends DefaultHandler { 035 036 /** 037 * Standart Sax Parser 038 */ 039 public final static String DEFAULT_SAX_PARSER="org.apache.xerces.parsers.SAXParser"; 040 /** 041 * Field <code>TYPE_CFML</code> 042 */ 043 public final static short TYPE_CFML=0; 044 /** 045 * Field <code>TYPE_JSP</code> 046 */ 047 public final static short TYPE_JSP=1; 048 049 //private short type=TYPE_CFML; 050 051 private XMLReader xmlReader; 052 053 private static Map hashLib=new HashTable(); 054 private static TagLib systemTLD; 055 private TagLib lib=new TagLib(); 056 057 private TagLibTag tag; 058 private boolean insideTag=false; 059 private boolean insideScript=false; 060 061 private TagLibTagAttr att; 062 private boolean insideAtt=false; 063 064 private String inside; 065 private StringBuffer content=new StringBuffer(); 066 private TagLibTagScript script; 067 // System default tld 068 private final static String TLD_1_0= "/resource/tld/web-cfmtaglibrary_1_0"; 069 070 071 /** 072 * Privater Konstruktor, der als Eingabe die TLD als File Objekt erhaelt. 073 * @param saxParser String Klassenpfad zum Sax Parser. 074 * @param file File Objekt auf die TLD. 075 * @throws TagLibException 076 * @throws IOException 077 */ 078 private TagLibFactory(String saxParser,Resource res) throws TagLibException { 079 Reader r=null; 080 try { 081 InputSource is=new InputSource(r=IOUtil.getReader(res.getInputStream(), null)); 082 is.setSystemId(res.getPath()); 083 init(saxParser,is); 084 } catch (IOException e) { 085 throw new TagLibException(e); 086 } 087 finally { 088 IOUtil.closeEL(r); 089 } 090 } 091 092 /** 093 * Privater Konstruktor, der als Eingabe die TLD als File Objekt erhaelt. 094 * @param saxParser String Klassenpfad zum Sax Parser. 095 * @param file File Objekt auf die TLD. 096 * @throws TagLibException 097 */ 098 private TagLibFactory(String saxParser,InputStream stream) throws TagLibException { 099 try { 100 InputSource is=new InputSource(IOUtil.getReader(stream, SystemUtil.getCharset())); 101 //is.setSystemId(file.toString()); 102 init(saxParser,is); 103 } catch (IOException e) { 104 throw new TagLibException(e); 105 } 106 } 107 108 /** 109 * Privater Konstruktor nur mit Sax Parser Definition, liest Default TLD vom System ein. 110 * @param saxParser String Klassenpfad zum Sax Parser. 111 * @throws TagLibException 112 */ 113 private TagLibFactory(String saxParser) throws TagLibException { 114 InputSource is=new InputSource(this.getClass().getResourceAsStream(TLD_1_0) ); 115 init(saxParser,is); 116 lib.setIsCore(true); 117 } 118 119 /** 120 * Generelle Initialisierungsmetode der Konstruktoren. 121 * @param saxParser String Klassenpfad zum Sax Parser. 122 * @param is InputStream auf die TLD. 123 * @throws TagLibException 124 */ 125 private void init(String saxParser,InputSource is) throws TagLibException { 126 //print.dumpStack(); 127 try { 128 xmlReader=XMLUtil.createXMLReader(saxParser); 129 xmlReader.setContentHandler(this); 130 xmlReader.setErrorHandler(this); 131 xmlReader.setEntityResolver(new TagLibEntityResolver()); 132 xmlReader.parse(is); 133 } 134 catch (IOException e) { 135 136 //String fileName=is.getSystemId(); 137 //String message="IOException: "; 138 //if(fileName!=null) message+="In File ["+fileName+"], "; 139 throw new TagLibException(e); 140 } 141 catch (SAXException e) { 142 e.printStackTrace(); 143 //String fileName=is.getSystemId(); 144 //String message="SAXException: "; 145 //if(fileName!=null) message+="In File ["+fileName+"], "; 146 throw new TagLibException(e); 147 } 148 149 } 150 151 /** 152 * Geerbte Methode von org.xml.sax.ContentHandler, 153 * wird bei durchparsen des XML, beim Auftreten eines Start-Tag aufgerufen. 154 * 155 * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes) 156 */ 157 public void startElement(String uri, String name, String qName, Attributes atts) { 158 159 inside=qName; 160 161 162 if(qName.equals("tag")) startTag(); 163 else if(qName.equals("attribute")) startAtt(); 164 else if(qName.equals("script")) startScript(); 165 166 } 167 168 /** 169 * Geerbte Methode von org.xml.sax.ContentHandler, 170 * wird bei durchparsen des XML, beim auftreten eines End-Tag aufgerufen. 171 * 172 * @see org.xml.sax.ContentHandler#endElement(String, String, String) 173 */ 174 public void endElement(String uri, String name, String qName) { 175 setContent(content.toString().trim()); 176 content=new StringBuffer(); 177 inside=""; 178 /* 179 if(tag!=null && tag.getName().equalsIgnoreCase("input")) { 180 print.ln(tag.getName()+"-"+att.getName()+":"+inside+"-"+insideTag+"-"+insideAtt); 181 182 } 183 */ 184 if(qName.equals("tag")) endTag(); 185 else if(qName.equals("attribute")) endAtt(); 186 else if(qName.equals("script")) endScript(); 187 } 188 189 190 /** 191 * Geerbte Methode von org.xml.sax.ContentHandler, 192 * wird bei durchparsen des XML, zum einlesen des Content eines Body Element aufgerufen. 193 * 194 * @see org.xml.sax.ContentHandler#characters(char[], int, int) 195 */ 196 public void characters (char ch[], int start, int length) { 197 content.append(new String(ch,start,length)); 198 } 199 200 private void setContent(String value) { 201 if(insideTag) { 202 // Att Args 203 if(insideAtt) { 204 // description? 205 // Name 206 if(inside.equals("name")) att.setName(value); 207 // Required 208 else if(inside.equals("required")) 209 att.setRequired(Caster.toBooleanValue(value,false)); 210 // Rtexprvalue 211 else if(inside.equals("rtexprvalue")) 212 att.setRtexpr(Caster.toBooleanValue(value,false)); 213 // Type 214 else if(inside.equals("type")) att.setType(value); 215 // Default-Value 216 else if(inside.equals("default-value")) att.setDefaultValue(value); 217 // status 218 else if(inside.equals("status")) att.setStatus(toStatus(value)); 219 // Description 220 else if(inside.equals("description")) att.setDescription(value); 221 // No-Name 222 else if(inside.equals("noname")) att.setNoname(Caster.toBooleanValue(value,false)); 223 // default 224 else if(inside.equals("default")) att.isDefault(Caster.toBooleanValue(value,false)); 225 else if(inside.equals("script-support")) att.setScriptSupport(value); 226 } 227 else if(insideScript) { 228 // type 229 if(inside.equals("type")) script.setType(value); 230 if(inside.equals("rtexprvalue")) script.setRtexpr(Caster.toBooleanValue(value,false)); 231 if(inside.equals("context")) script.setContext(value); 232 233 } 234 // Tag Args 235 else { 236 // TODO TEI-class 237 // Name 238 if(inside.equals("name")) {tag.setName(value);} 239 // TAG - Class 240 else if(inside.equals("tag-class")) tag.setTagClass(value); 241 else if(inside.equals("tagclass")) tag.setTagClass(value); 242 // status 243 else if(inside.equals("status")) tag.setStatus(toStatus(value)); 244 // TAG - description 245 else if(inside.equals("description")) tag.setDescription(value); 246 // TTE - Class 247 else if(inside.equals("tte-class")) tag.setTteClass(value); 248 // TTT - Class 249 else if(inside.equals("ttt-class")) tag.setTttClass(value); 250 // TDBT - Class 251 else if(inside.equals("tdbt-class")) tag.setTdbtClass(value); 252 // TDBT - Class 253 else if(inside.equals("att-class")) tag.setAttributeEvaluatorClassName(value); 254 // Body Content 255 else if(inside.equals("body-content") || inside.equals("bodycontent")) { 256 tag.setBodyContent(value); 257 } 258 //allow-removing-literal 259 else if(inside.equals("allow-removing-literal")) { 260 tag.setAllowRemovingLiteral(Caster.toBooleanValue(value,false)); 261 } 262 else if(inside.equals("att-default-value")) tag.setAttributeDefaultValue(value); 263 264 265 266 // Handle Exceptions 267 else if(inside.equals("handle-exception")) { 268 tag.setHandleExceptions(Caster.toBooleanValue(value,false)); 269 } 270 // Appendix 271 else if(inside.equals("appendix")) { 272 tag.setAppendix(Caster.toBooleanValue(value,false)); 273 } 274 // Body rtexprvalue 275 else if(inside.equals("body-rtexprvalue")) { 276 tag.setParseBody(Caster.toBooleanValue(value,false)); 277 } 278 // Att - min 279 else if(inside.equals("attribute-min")) 280 tag.setMin(Integer.parseInt(value)); 281 // Att - max 282 else if(inside.equals("attribute-max")) 283 tag.setMax(Integer.parseInt(value)); 284 // Att Type 285 else if(inside.equals("attribute-type")) { 286 int type=TagLibTag.ATTRIBUTE_TYPE_FIXED; 287 if(value.toLowerCase().equals("fix"))type=TagLibTag.ATTRIBUTE_TYPE_FIXED; 288 else if(value.toLowerCase().equals("fixed"))type=TagLibTag.ATTRIBUTE_TYPE_FIXED; 289 else if(value.toLowerCase().equals("dynamic"))type=TagLibTag.ATTRIBUTE_TYPE_DYNAMIC; 290 else if(value.toLowerCase().equals("noname"))type=TagLibTag.ATTRIBUTE_TYPE_NONAME; 291 else if(value.toLowerCase().equals("mixed"))type=TagLibTag.ATTRIBUTE_TYPE_MIXED; 292 else if(value.toLowerCase().equals("fulldynamic"))type=TagLibTag.ATTRIBUTE_TYPE_DYNAMIC;// deprecated 293 tag.setAttributeType(type); 294 } 295 } 296 } 297 // Tag Lib 298 else { 299 // TagLib Typ 300 if(inside.equals("jspversion")) { 301 //type=TYPE_JSP; 302 lib.setType("jsp"); 303 } 304 else if(inside.equals("cfml-version")) { 305 //type=TYPE_CFML; 306 lib.setType("cfml"); 307 } 308 309 310 // EL Class 311 else if(inside.equals("el-class")) lib.setELClass(value); 312 // Name-Space 313 else if(inside.equals("name-space")) lib.setNameSpace(value); 314 // Name Space Sep 315 else if(inside.equals("name-space-separator")) lib.setNameSpaceSeperator(value); 316 // short-name 317 else if(inside.equals("short-name")) lib.setShortName(value); 318 else if(inside.equals("shortname")) lib.setShortName(value); 319 // display-name 320 else if(inside.equals("display-name")) lib.setDisplayName(value); 321 else if(inside.equals("displayname")) lib.setDisplayName(value); 322 // ignore-unknow-tags 323 else if(inside.equals("ignore-unknow-tags")) lib.setIgnoreUnknowTags(Caster.toBooleanValue(value,false)); 324 325 326 else if(inside.equals("uri")) { 327 try { 328 lib.setUri(value); 329 } catch (URISyntaxException e) {} 330 } 331 else if(inside.equals("description")) lib.setDescription(value); 332 333 } 334 } 335 336 337 /** 338 * Wird jedesmal wenn das Tag tag beginnt aufgerufen, um intern in einen anderen Zustand zu gelangen. 339 */ 340 private void startTag() { 341 tag=new TagLibTag(lib); 342 insideTag=true; 343 } 344 345 /** 346 * Wird jedesmal wenn das Tag tag endet aufgerufen, um intern in einen anderen Zustand zu gelangen. 347 */ 348 private void endTag() { 349 lib.setTag(tag); 350 insideTag=false; 351 } 352 353 private void startScript() { 354 script=new TagLibTagScript(tag); 355 insideScript=true; 356 } 357 358 /** 359 * Wird jedesmal wenn das Tag tag endet aufgerufen, um intern in einen anderen Zustand zu gelangen. 360 */ 361 private void endScript() { 362 tag.setScript(script); 363 insideScript=false; 364 } 365 366 367 368 /** 369 * Wird jedesmal wenn das Tag attribute beginnt aufgerufen, um intern in einen anderen Zustand zu gelangen. 370 */ 371 private void startAtt() { 372 att=new TagLibTagAttr(tag); 373 insideAtt=true; 374 } 375 376 377 /** 378 * Wird jedesmal wenn das Tag tag endet aufgerufen, um intern in einen anderen Zustand zu gelangen. 379 */ 380 private void endAtt() { 381 tag.setAttribute(att); 382 insideAtt=false; 383 } 384 385 /** 386 * Gibt die interne TagLib zurueck. 387 * @return Interne Repraesentation der zu erstellenden TagLib. 388 */ 389 private TagLib getLib() { 390 return lib; 391 } 392 393 /** 394 * TagLib werden innerhalb der Factory in einer HashMap gecacht, 395 * so das diese einmalig von der Factory geladen werden. 396 * Diese Methode gibt eine gecachte TagLib anhand dessen key zurueck, 397 * falls diese noch nicht im Cache existiert, gibt die Methode null zurueck. 398 * 399 * @param key Absoluter Filepfad zur TLD. 400 * @return TagLib 401 */ 402 private static TagLib getHashLib(String key) { 403 return (TagLib)hashLib.get(key); 404 } 405 406 /** 407 * Laedt mehrere TagLib's die innerhalb eines Verzeichnisses liegen. 408 * @param dir Verzeichnis im dem die TagLib's liegen. 409 * @return TagLib's als Array 410 * @throws TagLibException 411 */ 412 public static TagLib[] loadFromDirectory(Resource dir) throws TagLibException { 413 return loadFromDirectory(dir,DEFAULT_SAX_PARSER); 414 } 415 416 /** 417 * Laedt mehrere TagLib's die innerhalb eines Verzeichnisses liegen. 418 * @param dir Verzeichnis im dem die TagLib's liegen. 419 * @param saxParser Definition des Sax Parser mit dem die TagLib's eingelesen werden sollen. 420 * @return TagLib's als Array 421 * @throws TagLibException 422 */ 423 public static TagLib[] loadFromDirectory(Resource dir,String saxParser) throws TagLibException { 424 if(!dir.isDirectory())return new TagLib[0]; 425 ArrayList<TagLib> arr=new ArrayList<TagLib>(); 426 427 Resource[] files=dir.listResources(new ExtensionResourceFilter("tld")); 428 for(int i=0;i<files.length;i++) { 429 if(files[i].isFile()) 430 arr.add(TagLibFactory.loadFromFile(files[i],saxParser)); 431 432 } 433 return arr.toArray(new TagLib[arr.size()]); 434 } 435 436 /** 437 * Laedt eine einzelne TagLib. 438 * @param file TLD die geladen werden soll. 439 * @return TagLib 440 * @throws TagLibException 441 */ 442 public static TagLib loadFromFile(Resource res) throws TagLibException { 443 return loadFromFile(res,DEFAULT_SAX_PARSER); 444 } 445 446 /** 447 * Laedt eine einzelne TagLib. 448 * @param file TLD die geladen werden soll. 449 * @return TagLib 450 * @throws TagLibException 451 */ 452 public static TagLib loadFromStream(InputStream is) throws TagLibException { 453 return loadFromStream(is,DEFAULT_SAX_PARSER); 454 } 455 456 /** 457 * Laedt eine einzelne TagLib. 458 * @param file TLD die geladen werden soll. 459 * @param saxParser Definition des Sax Parser mit dem die TagLib eingelsesen werden soll. 460 * @return TagLib 461 * @throws TagLibException 462 */ 463 public static TagLib loadFromFile(Resource res,String saxParser) throws TagLibException { 464 465 // Read in XML 466 TagLib lib=TagLibFactory.getHashLib(ResourceUtil.getCanonicalPathEL(res)); 467 if(lib==null) { 468 lib=new TagLibFactory(saxParser,res).getLib(); 469 TagLibFactory.hashLib.put(ResourceUtil.getCanonicalPathEL(res),lib); 470 } 471 lib.setSource(res.toString()); 472 return lib; 473 } 474 /** 475 * Laedt eine einzelne TagLib. 476 * @param file TLD die geladen werden soll. 477 * @param saxParser Definition des Sax Parser mit dem die TagLib eingelsesen werden soll. 478 * @return TagLib 479 * @throws TagLibException 480 */ 481 public static TagLib loadFromStream(InputStream is,String saxParser) throws TagLibException { 482 return new TagLibFactory(saxParser,is).getLib(); 483 } 484 485 /** 486 * Laedt die Systeminterne TLD. 487 * @return FunctionLib 488 * @throws TagLibException 489 */ 490 public static TagLib loadFromSystem() throws TagLibException { 491 return loadFromSystem(DEFAULT_SAX_PARSER); 492 } 493 494 /** 495 * Laedt die Systeminterne TLD. 496 * @param saxParser Definition des Sax Parser mit dem die FunctionLib eingelsesen werden soll. 497 * @return FunctionLib 498 * @throws TagLibException 499 */ 500 public static TagLib loadFromSystem(String saxParser) throws TagLibException { 501 if(systemTLD==null) 502 systemTLD=new TagLibFactory(saxParser).getLib(); 503 return systemTLD; 504 } 505 506 public static TagLib[] loadFrom(Resource res) throws TagLibException { 507 if(res.isDirectory())return loadFromDirectory(res); 508 if(res.isFile()) return new TagLib[]{loadFromFile(res)}; 509 throw new TagLibException("can not load tag library descriptor from ["+res+"]"); 510 } 511 512 513 /** 514 * return one FunctionLib contain content of all given Function Libs 515 * @param tlds 516 * @return combined function lib 517 */ 518 public static TagLib combineTLDs(TagLib[] tlds){ 519 TagLib tl = new TagLib(); 520 if(ArrayUtil.isEmpty(tlds)) return tl; 521 522 setAttributes(tlds[0],tl); 523 524 // add functions 525 for(int i=0;i<tlds.length;i++){ 526 copyTags(tlds[i],tl); 527 } 528 return tl; 529 } 530 531 public static TagLib combineTLDs(Set tlds){ 532 TagLib newTL = new TagLib(),tmp; 533 if(tlds.size()==0) return newTL ; 534 535 Iterator it = tlds.iterator(); 536 int count=0; 537 while(it.hasNext()){ 538 tmp=(TagLib) it.next(); 539 if(count++==0) setAttributes(tmp,newTL); 540 copyTags(tmp,newTL); 541 } 542 return newTL; 543 } 544 545 private static void setAttributes(TagLib extTL, TagLib newTL) { 546 newTL.setDescription(extTL.getDescription()); 547 newTL.setDisplayName(extTL.getDisplayName()); 548 newTL.setELClass(extTL.getELClass()); 549 newTL.setIsCore(extTL.isCore()); 550 newTL.setNameSpace(extTL.getNameSpace()); 551 newTL.setNameSpaceSeperator(extTL.getNameSpaceSeparator()); 552 newTL.setShortName(extTL.getShortName()); 553 newTL.setSource(extTL.getSource()); 554 newTL.setType(extTL.getType()); 555 newTL.setUri(extTL.getUri()); 556 557 } 558 559 private static void copyTags(TagLib extTL, TagLib newTL) { 560 Iterator it = extTL.getTags().entrySet().iterator(); 561 TagLibTag tlt; 562 while(it.hasNext()){ 563 tlt= (TagLibTag) ((Map.Entry)it.next()).getValue(); // TODO function must be duplicated because it gets a new FunctionLib assigned 564 newTL.setTag(tlt); 565 } 566 } 567 568 569 public static short toStatus(String value) { 570 value=value.trim().toLowerCase(); 571 if("deprecated".equals(value)) return TagLib.STATUS_DEPRECATED; 572 if("dep".equals(value)) return TagLib.STATUS_DEPRECATED; 573 574 if("unimplemented".equals(value)) return TagLib.STATUS_UNIMPLEMENTED; 575 if("unimplemeted".equals(value)) return TagLib.STATUS_UNIMPLEMENTED; 576 if("notimplemented".equals(value)) return TagLib.STATUS_UNIMPLEMENTED; 577 if("not-implemented".equals(value)) return TagLib.STATUS_UNIMPLEMENTED; 578 if("hidden".equals(value)) return TagLib.STATUS_HIDDEN; 579 580 return TagLib.STATUS_IMPLEMENTED; 581 } 582 public static String toStatus(short value) { 583 switch(value){ 584 case TagLib.STATUS_DEPRECATED: return "deprecated"; 585 case TagLib.STATUS_UNIMPLEMENTED: return "unimplemeted"; 586 case TagLib.STATUS_HIDDEN: return "hidden"; 587 } 588 return "implemeted"; 589 } 590 591 } 592 593 594 595 596 597 598 599