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