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    }