package railo.transformer.library.function; import java.io.IOException; import java.io.Reader; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import railo.commons.io.IOUtil; import railo.commons.io.res.Resource; import railo.commons.io.res.filter.ExtensionResourceFilter; import railo.commons.io.res.util.ResourceUtil; import railo.runtime.op.Caster; import railo.runtime.text.xml.XMLUtil; import railo.runtime.type.util.ArrayUtil; import railo.transformer.library.tag.TagLibFactory; /** * * Die FunctionLibFactory ist der Produzent fuer eine oder mehrere FunctionLib, * d.H. ueber statische Methoden (get, getDir) koennen FunctionLibs geladen werden. * Die FunctionLibFactory erbt sich vom DefaultHandler. */ public final class FunctionLibFactory extends DefaultHandler { private XMLReader xmlReader; //private File file; private boolean insideFunction=false,insideAttribute=false,insideReturn=false; private String inside; private StringBuffer content=new StringBuffer(); /** * Definiert den Default SAX Parser */ public final static String DEFAULT_SAX_PARSER="org.apache.xerces.parsers.SAXParser"; private static Map<String,FunctionLib> hashLib=new HashMap<String,FunctionLib>(); private static FunctionLib systemFLD; private FunctionLib lib=new FunctionLib(); private FunctionLibFunction function; private FunctionLibFunctionArg arg; private final static String FLD_1_0= "/resource/fld/web-cfmfunctionlibrary_1_0"; /** * Privater Konstruktor, der als Eingabe die FLD als InputStream erhaelt. * @param saxParser String Klassenpfad zum Sax Parser. * @param is InputStream auf die TLD. * @throws FunctionLibException private FunctionLibFactory(String saxParser,InputSource is) throws FunctionLibException { super(); init(saxParser,is); }*/ /** * Privater Konstruktor, der als Eingabe die FLD als File Objekt erhaelt. * @param saxParser String Klassenpfad zum Sax Parser. * @param file File Objekt auf die TLD. * @throws FunctionLibException */ private FunctionLibFactory(String saxParser,Resource file) throws FunctionLibException { super(); Reader r=null; try { init(saxParser,new InputSource(r=IOUtil.getReader(file.getInputStream(), (Charset)null))); } catch (IOException e) { throw new FunctionLibException("File not found: "+e.getMessage()); } finally { IOUtil.closeEL(r); } } /** * Privater Konstruktor nur mit Sax Parser Definition, liest Default FLD vom System ein. * @param saxParser String Klassenpfad zum Sax Parser. * @throws FunctionLibException */ private FunctionLibFactory(String saxParser) throws FunctionLibException { super(); InputSource is=new InputSource(this.getClass().getResourceAsStream(FLD_1_0) ); init(saxParser,is); } /** * Generelle Initialisierungsmetode der Konstruktoren. * @param saxParser String Klassenpfad zum Sax Parser. * @param is InputStream auf die TLD. * @throws FunctionLibException */ private void init(String saxParser,InputSource is) throws FunctionLibException { try { xmlReader=XMLUtil.createXMLReader(saxParser); xmlReader.setContentHandler(this); xmlReader.setErrorHandler(this); xmlReader.setEntityResolver(new FunctionLibEntityResolver()); xmlReader.parse(is); } catch (IOException e) { throw new FunctionLibException("IO Exception: "+e.getMessage()); } catch (SAXException e) { throw new FunctionLibException("SaxException: "+e.getMessage()); } } /** * Geerbte Methode von org.xml.sax.ContentHandler, * wird bei durchparsen des XML, beim Auftreten eines Start-Tag aufgerufen. * * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes) */ public void startElement (String uri, String name, String qName, Attributes atts) { // Start Function inside=qName; if(qName.equals("function")) startFunction(); else if(qName.equals("argument")) startArg(); else if(qName.equals("return")) startReturn(); } /** * Geerbte Methode von org.xml.sax.ContentHandler, * wird bei durchparsen des XML, beim auftreten eines End-Tag aufgerufen. * * @see org.xml.sax.ContentHandler#endElement(String, String, String) */ public void endElement (String uri, String name, String qName) { setContent(content.toString().trim()); content=new StringBuffer(); inside=""; if(qName.equals("function")) endFunction(); else if(qName.equals("argument")) endArg(); else if(qName.equals("return")) endReturn(); } /** * Wird jedesmal wenn das Tag function beginnt aufgerufen, * um intern in einen anderen Zustand zu gelangen. */ private void startFunction() { function=new FunctionLibFunction(); insideFunction=true; } /** * Wird jedesmal wenn das Tag function endet aufgerufen, * um intern in einen anderen Zustand zu gelangen. */ private void endFunction() { lib.setFunction(function); insideFunction=false; } /** * Wird jedesmal wenn das Tag argument beginnt aufgerufen, * um intern in einen anderen Zustand zu gelangen. */ private void startArg() { insideAttribute=true; arg=new FunctionLibFunctionArg(); } /** * Wird jedesmal wenn das Tag argument endet aufgerufen, * um intern in einen anderen Zustand zu gelangen. */ private void endArg() { function.setArg(arg); insideAttribute=false; } /** * Wird jedesmal wenn das Tag return beginnt aufgerufen, * um intern in einen anderen Zustand zu gelangen. */ private void startReturn() { insideReturn=true; } /** * Wird jedesmal wenn das Tag return endet aufgerufen, * um intern in einen anderen Zustand zu gelangen. */ private void endReturn() { insideReturn=false; } /** * Geerbte Methode von org.xml.sax.ContentHandler, * wird bei durchparsen des XML, zum einlesen des Content eines Body Element aufgerufen. * * @see org.xml.sax.ContentHandler#characters(char[], int, int) */ public void characters (char ch[], int start, int length) { content.append(new String(ch,start,length)); } private void setContent(String value) { if(insideFunction) { // Attributes Value if(insideAttribute) { if(inside.equals("type")) arg.setType(value); else if(inside.equals("name")) arg.setName(value); else if(inside.equals("default")) arg.setDefaultValue(value); else if(inside.equals("default-value")) arg.setDefaultValue(value); // deprecated else if(inside.equals("status")) arg.setStatus(TagLibFactory.toStatus(value)); else if(inside.equals("description")) arg.setDescription(value); else if(inside.equals("alias")) arg.setAlias(value); else if(inside.equals("required")) { arg.setRequired(value); if(arg.isRequired()) function.setArgMin(function.getArgMin()+1); } } // Return Values else if(insideReturn) { if(inside.equals("type")) function.setReturn(value); } // Function Value else { if(inside.equals("name")) function.setName(value); else if(inside.equals("class")) function.setCls(value); else if(inside.equals("tte-class")) function.setTteClass(value); else if(inside.equals("description")) function.setDescription(value); else if(inside.equals("member-name")) function.setMemberName(value); else if(inside.equals("member-chaining")) function.setMemberChaining(Caster.toBooleanValue(value,false)); else if(inside.equals("status")) function.setStatus(TagLibFactory.toStatus(value)); else if(inside.equals("argument-type")) function.setArgType(value.equalsIgnoreCase("dynamic")?FunctionLibFunction.ARG_DYNAMIC:FunctionLibFunction.ARG_FIX); else if(inside.equals("argument-min")) function.setArgMin(Integer.parseInt(value)); else if(inside.equals("argument-max")) function.setArgMax(Integer.parseInt(value)); } } else { //function lib values if(inside.equals("flib-version")) lib.setVersion(value); else if(inside.equals("short-name")) lib.setShortName(value); else if(inside.equals("uri")) { try { lib.setUri(value); } catch (URISyntaxException e) {} } else if(inside.equals("display-name")) lib.setDisplayName(value); else if(inside.equals("description")) lib.setDescription(value); } } /** * Gibt die interne FunctionLib zurueck. * @return Interne Repraesentation der zu erstellenden FunctionLib. */ private FunctionLib getLib() { return lib; } /** * Laedt mehrere FunctionLib's die innerhalb eines Verzeichnisses liegen. * @param dir Verzeichnis im dem die FunctionLib's liegen. * @return FunctionLib's als Array * @throws FunctionLibException */ public static FunctionLib[] loadFromDirectory(Resource dir) throws FunctionLibException { return loadFromDirectory(dir,DEFAULT_SAX_PARSER); } /** * Laedt mehrere FunctionLib's die innerhalb eines Verzeichnisses liegen. * @param dir Verzeichnis im dem die FunctionLib's liegen. * @param saxParser Definition des Sax Parser mit dem die FunctionLib's eingelesen werden sollen. * @return FunctionLib's als Array * @throws FunctionLibException */ public static FunctionLib[] loadFromDirectory(Resource dir,String saxParser) throws FunctionLibException { if(!dir.isDirectory())return new FunctionLib[0]; ArrayList<FunctionLib> arr=new ArrayList<FunctionLib>(); Resource[] files=dir.listResources(new ExtensionResourceFilter("fld")); for(int i=0;i<files.length;i++) { if(files[i].isFile()) arr.add(FunctionLibFactory.loadFromFile(files[i],saxParser)); } return arr.toArray(new FunctionLib[arr.size()]); } /** * Laedt eine einzelne FunctionLib. * @param file FLD die geladen werden soll. * @return FunctonLib * @throws FunctionLibException */ public static FunctionLib loadFromFile(Resource file) throws FunctionLibException { return loadFromFile(file,DEFAULT_SAX_PARSER); } /** * Laedt eine einzelne FunctionLib. * @param res FLD die geladen werden soll. * @param saxParser Definition des Sax Parser mit dem die FunctionLib eingelsesen werden soll. * @return FunctionLib * @throws FunctionLibException */ public static FunctionLib loadFromFile(Resource res,String saxParser) throws FunctionLibException { // Read in XML FunctionLib lib=FunctionLibFactory.hashLib.get(ResourceUtil.getCanonicalPathEL(res));//getHashLib(file.getAbsolutePath()); if(lib==null) { lib=new FunctionLibFactory(saxParser,res).getLib(); FunctionLibFactory.hashLib.put(ResourceUtil.getCanonicalPathEL(res),lib); } lib.setSource(res.toString()); return lib; } /** * Laedt die Systeminterne FLD. * @return FunctionLib * @throws FunctionLibException */ public static FunctionLib loadFromSystem() throws FunctionLibException { return loadFromSystem(DEFAULT_SAX_PARSER); } /** * Laedt die Systeminterne FLD. * @param saxParser Definition des Sax Parser mit dem die FunctionLib eingelsesen werden soll. * @return FunctionLib * @throws FunctionLibException */ public static FunctionLib loadFromSystem(String saxParser) throws FunctionLibException { if(systemFLD==null) systemFLD=new FunctionLibFactory(saxParser).getLib(); return systemFLD; } /** * return one FunctionLib contain content of all given Function Libs * @param flds * @return combined function lib */ public static FunctionLib combineFLDs(FunctionLib[] flds){ FunctionLib fl = new FunctionLib(); if(ArrayUtil.isEmpty(flds)) return fl ; setAttributes(flds[0],fl); // add functions for(int i=0;i<flds.length;i++){ copyFunctions(flds[i],fl); } return fl; } public static FunctionLib combineFLDs(Set flds){ FunctionLib newFL = new FunctionLib(),tmp; if(flds.size()==0) return newFL ; Iterator it = flds.iterator(); int count=0; while(it.hasNext()){ tmp=(FunctionLib) it.next(); if(count++==0) setAttributes(tmp,newFL); copyFunctions(tmp,newFL); } return newFL; } /** * copy function from one FunctionLib to a other * @param extFL * @param newFL */ private static void copyFunctions(FunctionLib extFL, FunctionLib newFL) { Iterator<Entry<String, FunctionLibFunction>> it = extFL.getFunctions().entrySet().iterator(); FunctionLibFunction flf; while(it.hasNext()){ flf= it.next().getValue(); // TODO function must be duplicated because it gets a new FunctionLib assigned newFL.setFunction(flf); } } /** * copy attributes from old fld to the new * @param extFL * @param newFL */ private static void setAttributes(FunctionLib extFL, FunctionLib newFL) { newFL.setDescription(extFL.getDescription()); newFL.setDisplayName(extFL.getDisplayName()); newFL.setShortName(extFL.getShortName()); newFL.setUri(extFL.getUri()); newFL.setVersion(extFL.getVersion()); } }