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