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