001 package railo.transformer.library.function; 002 003 import java.io.IOException; 004 import java.io.Reader; 005 import java.net.URISyntaxException; 006 import java.util.ArrayList; 007 import java.util.Iterator; 008 import java.util.Map; 009 import java.util.Set; 010 011 import org.xml.sax.Attributes; 012 import org.xml.sax.InputSource; 013 import org.xml.sax.SAXException; 014 import org.xml.sax.XMLReader; 015 import org.xml.sax.helpers.DefaultHandler; 016 import org.xml.sax.helpers.XMLReaderFactory; 017 018 import railo.commons.collections.HashTable; 019 import railo.commons.io.IOUtil; 020 import railo.commons.io.res.Resource; 021 import railo.commons.io.res.filter.ExtensionResourceFilter; 022 import railo.commons.io.res.util.ResourceUtil; 023 import railo.runtime.type.util.ArrayUtil; 024 import railo.transformer.library.tag.TagLibFactory; 025 026 027 028 /** 029 * 030 * Die FunctionLibFactory ist der Produzent f�r eine oder mehrere FunctionLib, 031 * d.H. �ber statische Methoden (get, getDir) k�nnen FunctionLibs geladen werden. 032 * Die FunctionLibFactory erbt sich vom DefaultHandler. 033 */ 034 public final class FunctionLibFactory extends DefaultHandler { 035 private XMLReader xmlReader; 036 //private File file; 037 private boolean insideFunction=false,insideAttribute=false,insideReturn=false; 038 private String inside; 039 private StringBuffer content=new StringBuffer(); 040 /** 041 * Definiert den Default SAX Parser 042 */ 043 public final static String DEFAULT_SAX_PARSER="org.apache.xerces.parsers.SAXParser"; 044 045 046 private static Map hashLib=new HashTable(); 047 private static FunctionLib systemFLD; 048 private FunctionLib lib=new FunctionLib(); 049 private FunctionLibFunction function; 050 051 private FunctionLibFunctionArg arg; 052 053 private final static String FLD_1_0= "/resource/fld/web-cfmfunctionlibrary_1_0"; 054 055 056 057 /** 058 * Privater Konstruktor, der als Eingabe die FLD als InputStream erh�lt. 059 * @param saxParser String Klassenpfad zum Sax Parser. 060 * @param is InputStream auf die TLD. 061 * @throws FunctionLibException 062 063 private FunctionLibFactory(String saxParser,InputSource is) throws FunctionLibException { 064 super(); 065 init(saxParser,is); 066 }*/ 067 068 /** 069 * Privater Konstruktor, der als Eingabe die FLD als File Objekt erh�lt. 070 * @param saxParser String Klassenpfad zum Sax Parser. 071 * @param file File Objekt auf die TLD. 072 * @throws FunctionLibException 073 */ 074 private FunctionLibFactory(String saxParser,Resource file) throws FunctionLibException { 075 super(); 076 Reader r=null; 077 try { 078 init(saxParser,new InputSource(r=IOUtil.getReader(file.getInputStream(), null))); 079 } catch (IOException e) { 080 throw new FunctionLibException("File not found: "+e.getMessage()); 081 } 082 finally { 083 IOUtil.closeEL(r); 084 } 085 } 086 087 /** 088 * Privater Konstruktor nur mit Sax Parser Definition, liest Default FLD vom System ein. 089 * @param saxParser String Klassenpfad zum Sax Parser. 090 * @throws FunctionLibException 091 */ 092 private FunctionLibFactory(String saxParser) throws FunctionLibException { 093 super(); 094 InputSource is=new InputSource(this.getClass().getResourceAsStream(FLD_1_0) ); 095 init(saxParser,is); 096 } 097 098 /** 099 * Generelle Initialisierungsmetode der Konstruktoren. 100 * @param saxParser String Klassenpfad zum Sax Parser. 101 * @param is InputStream auf die TLD. 102 * @throws FunctionLibException 103 */ 104 private void init(String saxParser,InputSource is) throws FunctionLibException { 105 try { 106 xmlReader=XMLReaderFactory.createXMLReader(saxParser); 107 xmlReader.setContentHandler(this); 108 xmlReader.setErrorHandler(this); 109 xmlReader.setEntityResolver(new FunctionLibEntityResolver()); 110 xmlReader.parse(is); 111 } catch (IOException e) { 112 113 throw new FunctionLibException("IO Exception: "+e.getMessage()); 114 } catch (SAXException e) { 115 throw new FunctionLibException("SaxException: "+e.getMessage()); 116 } 117 118 } 119 120 /** 121 * Geerbte Methode von org.xml.sax.ContentHandler, 122 * wird bei durchparsen des XML, beim Auftreten eines Start-Tag aufgerufen. 123 * 124 * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes) 125 */ 126 public void startElement (String uri, String name, 127 String qName, Attributes atts) { 128 // Start Function 129 inside=qName; 130 if(qName.equals("function")) startFunction(); 131 else if(qName.equals("argument")) startArg(); 132 else if(qName.equals("return")) startReturn(); 133 } 134 135 /** 136 * Geerbte Methode von org.xml.sax.ContentHandler, 137 * wird bei durchparsen des XML, beim auftreten eines End-Tag aufgerufen. 138 * 139 * @see org.xml.sax.ContentHandler#endElement(String, String, String) 140 */ 141 public void endElement (String uri, String name, String qName) { 142 setContent(content.toString().trim()); 143 content=new StringBuffer(); 144 inside=""; 145 if(qName.equals("function")) endFunction(); 146 else if(qName.equals("argument")) endArg(); 147 else if(qName.equals("return")) endReturn(); 148 149 } 150 151 /** 152 * Wird jedesmal wenn das Tag function beginnt aufgerufen, 153 * um intern in einen anderen Zustand zu gelangen. 154 */ 155 private void startFunction() { 156 function=new FunctionLibFunction(); 157 insideFunction=true; 158 } 159 160 /** 161 * Wird jedesmal wenn das Tag function endet aufgerufen, 162 * um intern in einen anderen Zustand zu gelangen. 163 */ 164 private void endFunction() { 165 lib.setFunction(function); 166 insideFunction=false; 167 } 168 169 /** 170 * Wird jedesmal wenn das Tag argument beginnt aufgerufen, 171 * um intern in einen anderen Zustand zu gelangen. 172 */ 173 private void startArg() { 174 insideAttribute=true; 175 arg=new FunctionLibFunctionArg(); 176 } 177 178 /** 179 * Wird jedesmal wenn das Tag argument endet aufgerufen, 180 * um intern in einen anderen Zustand zu gelangen. 181 */ 182 private void endArg() { 183 function.setArg(arg); 184 insideAttribute=false; 185 } 186 187 /** 188 * Wird jedesmal wenn das Tag return beginnt aufgerufen, 189 * um intern in einen anderen Zustand zu gelangen. 190 */ 191 private void startReturn() { 192 insideReturn=true; 193 } 194 195 /** 196 * Wird jedesmal wenn das Tag return endet aufgerufen, 197 * um intern in einen anderen Zustand zu gelangen. 198 */ 199 private void endReturn() { 200 insideReturn=false; 201 } 202 203 /** 204 * Geerbte Methode von org.xml.sax.ContentHandler, 205 * wird bei durchparsen des XML, zum einlesen des Content eines Body Element aufgerufen. 206 * 207 * @see org.xml.sax.ContentHandler#characters(char[], int, int) 208 */ 209 public void characters (char ch[], int start, int length) { 210 content.append(new String(ch,start,length)); 211 } 212 213 private void setContent(String value) { 214 if(insideFunction) { 215 // Attributes Value 216 if(insideAttribute) { 217 if(inside.equals("type")) arg.setType(value); 218 else if(inside.equals("name")) arg.setName(value); 219 else if(inside.equals("default")) arg.setDefaultValue(value); 220 else if(inside.equals("default-value")) arg.setDefaultValue(value); // deprecated 221 else if(inside.equals("status")) arg.setStatus(TagLibFactory.toStatus(value)); 222 else if(inside.equals("description")) arg.setDescription(value); 223 else if(inside.equals("alias")) arg.setAlias(value); 224 225 else if(inside.equals("required")) { 226 arg.setRequired(value); 227 if(arg.isRequired()) 228 function.setArgMin(function.getArgMin()+1); 229 } 230 } 231 // Return Values 232 else if(insideReturn) { 233 if(inside.equals("type")) 234 function.setReturn(value); 235 } 236 // Function Value 237 else { 238 if(inside.equals("name")) 239 function.setName(value); 240 241 else if(inside.equals("class")) 242 function.setCls(value); 243 244 else if(inside.equals("tte-class")) 245 function.setTteClass(value); 246 247 248 else if(inside.equals("description")) 249 function.setDescription(value); 250 251 else if(inside.equals("status")) 252 function.setStatus(TagLibFactory.toStatus(value)); 253 254 else if(inside.equals("argument-type")) 255 function.setArgType(value.equalsIgnoreCase("dynamic")?FunctionLibFunction.ARG_DYNAMIC:FunctionLibFunction.ARG_FIX); 256 257 else if(inside.equals("argument-min")) 258 function.setArgMin(Integer.parseInt(value)); 259 260 else if(inside.equals("argument-max")) 261 function.setArgMax(Integer.parseInt(value)); 262 } 263 264 } 265 else { 266 //function lib values 267 if(inside.equals("flib-version")) lib.setVersion(value); 268 else if(inside.equals("short-name")) lib.setShortName(value); 269 else if(inside.equals("uri")) { 270 try { 271 lib.setUri(value); 272 } catch (URISyntaxException e) {} 273 } 274 else if(inside.equals("display-name")) lib.setDisplayName(value); 275 else if(inside.equals("description")) lib.setDescription(value); 276 } 277 } 278 279 /** 280 * Gibt die interne FunctionLib zur�ck. 281 * @return Interne Repr�sentation der zu erstellenden FunctionLib. 282 */ 283 private FunctionLib getLib() { 284 return lib; 285 } 286 287 /** 288 * L�dt mehrere FunctionLib's die innerhalb eines Verzeichnisses liegen. 289 * @param dir Verzeichnis im dem die FunctionLib's liegen. 290 * @return FunctionLib's als Array 291 * @throws FunctionLibException 292 */ 293 public static FunctionLib[] loadFromDirectory(Resource dir) throws FunctionLibException { 294 return loadFromDirectory(dir,DEFAULT_SAX_PARSER); 295 } 296 297 /** 298 * L�dt mehrere FunctionLib's die innerhalb eines Verzeichnisses liegen. 299 * @param dir Verzeichnis im dem die FunctionLib's liegen. 300 * @param saxParser Definition des Sax Parser mit dem die FunctionLib's eingelesen werden sollen. 301 * @return FunctionLib's als Array 302 * @throws FunctionLibException 303 */ 304 public static FunctionLib[] loadFromDirectory(Resource dir,String saxParser) throws FunctionLibException { 305 if(!dir.isDirectory())return new FunctionLib[0]; 306 ArrayList arr=new ArrayList(); 307 308 Resource[] files=dir.listResources(new ExtensionResourceFilter("fld")); 309 for(int i=0;i<files.length;i++) { 310 if(files[i].isFile()) 311 arr.add(FunctionLibFactory.loadFromFile(files[i],saxParser)); 312 } 313 314 return (FunctionLib[]) arr.toArray(new FunctionLib[arr.size()]); 315 } 316 /** 317 * L�dt eine einzelne FunctionLib. 318 * @param file FLD die geladen werden soll. 319 * @return FunctonLib 320 * @throws FunctionLibException 321 */ 322 public static FunctionLib loadFromFile(Resource file) throws FunctionLibException { 323 return loadFromFile(file,DEFAULT_SAX_PARSER); 324 } 325 326 /** 327 * L�dt eine einzelne FunctionLib. 328 * @param res FLD die geladen werden soll. 329 * @param saxParser Definition des Sax Parser mit dem die FunctionLib eingelsesen werden soll. 330 * @return FunctionLib 331 * @throws FunctionLibException 332 */ 333 public static FunctionLib loadFromFile(Resource res,String saxParser) throws FunctionLibException { 334 // Read in XML 335 FunctionLib lib=(FunctionLib) FunctionLibFactory.hashLib.get(ResourceUtil.getCanonicalPathEL(res));//getHashLib(file.getAbsolutePath()); 336 if(lib==null) { 337 lib=new FunctionLibFactory(saxParser,res).getLib(); 338 FunctionLibFactory.hashLib.put(ResourceUtil.getCanonicalPathEL(res),lib); 339 } 340 lib.setSource(res.toString()); 341 342 return lib; 343 } 344 345 /** 346 * L�dt die Systeminterne FLD. 347 * @return FunctionLib 348 * @throws FunctionLibException 349 */ 350 public static FunctionLib loadFromSystem() throws FunctionLibException { 351 return loadFromSystem(DEFAULT_SAX_PARSER); 352 } 353 354 /** 355 * L�dt die Systeminterne FLD. 356 * @param saxParser Definition des Sax Parser mit dem die FunctionLib eingelsesen werden soll. 357 * @return FunctionLib 358 * @throws FunctionLibException 359 */ 360 public static FunctionLib loadFromSystem(String saxParser) throws FunctionLibException { 361 if(systemFLD==null) 362 systemFLD=new FunctionLibFactory(saxParser).getLib(); 363 364 return systemFLD; 365 } 366 367 /** 368 * return one FunctionLib contain content of all given Function Libs 369 * @param flds 370 * @return combined function lib 371 */ 372 public static FunctionLib combineFLDs(FunctionLib[] flds){ 373 FunctionLib fl = new FunctionLib(); 374 if(ArrayUtil.isEmpty(flds)) return fl ; 375 376 setAttributes(flds[0],fl); 377 378 // add functions 379 for(int i=0;i<flds.length;i++){ 380 copyFunctions(flds[i],fl); 381 } 382 return fl; 383 } 384 385 public static FunctionLib combineFLDs(Set flds){ 386 FunctionLib newFL = new FunctionLib(),tmp; 387 if(flds.size()==0) return newFL ; 388 389 Iterator it = flds.iterator(); 390 int count=0; 391 while(it.hasNext()){ 392 tmp=(FunctionLib) it.next(); 393 if(count++==0) setAttributes(tmp,newFL); 394 copyFunctions(tmp,newFL); 395 } 396 return newFL; 397 } 398 399 /** 400 * copy function from one FunctionLib to a other 401 * @param extFL 402 * @param newFL 403 */ 404 private static void copyFunctions(FunctionLib extFL, FunctionLib newFL) { 405 Iterator it = extFL.getFunctions().entrySet().iterator(); 406 FunctionLibFunction flf; 407 while(it.hasNext()){ 408 flf= (FunctionLibFunction) ((Map.Entry)it.next()).getValue(); // TODO function must be duplicated because it gets a new FunctionLib assigned 409 newFL.setFunction(flf); 410 } 411 } 412 413 /** 414 * copy attributes from old fld to the new 415 * @param extFL 416 * @param newFL 417 */ 418 private static void setAttributes(FunctionLib extFL, FunctionLib newFL) { 419 newFL.setDescription(extFL.getDescription()); 420 newFL.setDisplayName(extFL.getDisplayName()); 421 newFL.setShortName(extFL.getShortName()); 422 newFL.setUri(extFL.getUri()); 423 newFL.setVersion(extFL.getVersion()); 424 } 425 426 }