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    }