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.runtime.functions.component;
020
021import java.io.File;
022import java.io.IOException;
023import java.nio.charset.Charset;
024import java.util.ArrayList;
025import java.util.HashSet;
026import java.util.Iterator;
027import java.util.Set;
028
029import lucee.commons.io.IOUtil;
030import lucee.commons.io.res.Resource;
031import lucee.commons.io.res.filter.ExtensionResourceFilter;
032import lucee.commons.io.res.util.ResourceUtil;
033import lucee.commons.lang.ExceptionUtil;
034import lucee.commons.lang.StringUtil;
035import lucee.runtime.Mapping;
036import lucee.runtime.PageContext;
037import lucee.runtime.PageContextImpl;
038import lucee.runtime.PageSource;
039import lucee.runtime.config.ConfigWebImpl;
040import lucee.runtime.exp.ApplicationException;
041import lucee.runtime.exp.PageException;
042import lucee.runtime.ext.function.Function;
043import lucee.runtime.op.Caster;
044import lucee.runtime.type.Array;
045import lucee.runtime.type.ArrayImpl;
046import lucee.runtime.type.util.ArrayUtil;
047import lucee.runtime.type.util.ListUtil;
048import lucee.transformer.bytecode.util.ASMUtil;
049
050public class ComponentListPackage implements Function {
051        
052        private static final long serialVersionUID = 6502632300879457687L;
053        
054        private static final ExtensionResourceFilter FILTER_CFC = new ExtensionResourceFilter(".cfc");
055        private static final ExtensionResourceFilter FILTER_CLASS = new ExtensionResourceFilter(".class");
056        private static final String[] EMPTY = new String[0];
057        
058
059        public static Array call(PageContext pc , String packageName) throws PageException {
060                Set<String> names;
061                try {
062                        names = _call(pc, packageName);
063                } catch (IOException e) {
064                        throw Caster.toPageException(e);
065                }
066                
067                Array arr=new ArrayImpl();
068                String name;
069                Iterator<String> it = names.iterator();
070                while(it.hasNext()){
071                        name=it.next();
072                        if(StringUtil.endsWithIgnoreCase(name, ".cfc")) {
073                                name=name.substring(0,name.length()-4);
074                        }
075                        arr.appendEL(name);
076                }
077                return arr;
078        }
079        
080        private static Set<String> _call(PageContext pc , String packageName) throws IOException, ApplicationException {
081                PageContextImpl pci=(PageContextImpl) pc;
082                ConfigWebImpl config = (ConfigWebImpl) pc.getConfig();
083                Set<String> rtn=null;
084                //var SEP=server.separator.file;
085                
086                // get enviroment configuration
087                boolean searchLocal = config.getComponentLocalSearch();
088                boolean searchRoot=config.getComponentRootSearch();
089                
090                String path=StringUtil.replace(packageName, ".", File.separator, false);
091                
092                // search local 
093                if(searchLocal) {
094                        //Resource dir=pc.getCurrentTemplatePageSource().getResourceTranslated(pc).getParentResource();
095                        //dir=dir.getRealResource(path);
096                        PageSource ps= pci.getRelativePageSourceExisting(path);
097                        if(ps!=null){
098                                Mapping mapping = ps.getMapping();
099                                String _path=ps.getRealpath();
100                                _path=ListUtil.trim(_path,"\\/");
101                                String[] list = _listMapping(pc,mapping,_path);
102                                if(!ArrayUtil.isEmpty(list)) rtn=add(rtn,list);
103                        }
104                }
105                
106                // check mappings (this includes the webroot)
107                if(searchRoot) {        
108                        String virtual="/"+StringUtil.replace(packageName, ".", "/", false);
109                        Mapping[] mappings = config.getMappings();
110                        Mapping mapping;
111                        String _path;
112                        String[] list;
113                        for(int i=0;i<mappings.length;i++){
114                                mapping=mappings[i];
115                                if(StringUtil.startsWithIgnoreCase(virtual, mapping.getVirtual()))  {
116                                        _path=ListUtil.trim(virtual.substring(mapping.getVirtual().length()),"\\/").trim(); 
117                                        _path=StringUtil.replace(_path, "/", File.separator, false);
118                                        list = _listMapping(pc,mapping,_path);
119                                        if(!ArrayUtil.isEmpty(list)) rtn=add(rtn,list);
120                                }
121                        }
122                }
123                
124                // check component mappings
125                Mapping[] mappings = config.getComponentMappings();
126                Mapping mapping;
127                String[] list;
128                for(int i=0;i<mappings.length;i++){
129                        mapping=mappings[i];
130                        list=_listMapping(pc,mapping,path);
131                        if(!ArrayUtil.isEmpty(list)) rtn=add(rtn,list);
132                }
133                
134                if(rtn==null)throw new ApplicationException("no package with name ["+packageName+"] found");
135                return rtn;
136        }
137
138        
139        private static Set<String> add(Set<String> set, String[] arr) {
140                if(set==null) set=new HashSet<String>();
141                for(int i=0;i<arr.length;i++){
142                        set.add(arr[i]);
143                }
144                return set;
145        }
146
147        private static String[] _listMapping(PageContext pc,Mapping mapping, String path) throws IOException{
148                if(mapping.isPhysicalFirst()) {
149                        // check physical
150                        String[] list = _listPhysical(path,mapping);
151                        if(!ArrayUtil.isEmpty(list)) return list;
152                        
153                        // check archive
154                        list=_listArchive(pc,path,mapping);
155                        if(!ArrayUtil.isEmpty(list)) return list;
156                }
157                else {
158                        // check archive
159                        String[] list = _listArchive(pc,path,mapping);
160                        if(!ArrayUtil.isEmpty(list)) return list;
161                        // check physical
162                        list=_listPhysical(path,mapping);
163                        if(!ArrayUtil.isEmpty(list)) return list;
164                }
165                return null;
166        }
167
168        private static String[] _listPhysical(String path, Mapping mapping){
169                Resource physical = mapping.getPhysical();
170                if(physical!=null) {
171                        Resource dir = physical.getRealResource(path);
172                        if(dir.isDirectory()) {
173                                return dir.list(FILTER_CFC);
174                        }
175                }
176                return EMPTY;
177        }
178        
179        private static String[]  _listArchive(PageContext pc,String path, Mapping mapping) throws IOException {
180                String packageName=StringUtil.replace(path, File.separator, ".", false);
181                Resource archive = mapping.getArchive();
182                if(archive!=null) {
183                        // TODO nor working with pathes with none ascci characters, eith none ascci characters, the java class path is renamed, so make sure you rename the path as well
184                        String strDir="zip://"+archive+"!"+File.separator+path;
185                        Resource dir = ResourceUtil.toResourceNotExisting(pc, strDir,true,false);
186                        
187                        if(dir.isDirectory()) {
188                                java.util.List<String> list=new ArrayList<String>();
189                                // we use the class files here to get the info, the source files are optional and perhaps not present.
190                                Resource[] children = dir.listResources(FILTER_CLASS);
191                                String className,c,sourceName=null;
192                                for(int i=0;i<children.length;i++){
193                                        className=children[i].getName();
194                                        className=className.substring(0,className.length()-6);
195                                        className=packageName+"."+className;
196                                        
197                                        try {
198                                                Class<?> clazz = mapping.getClassLoaderForArchive().loadClass(className);
199                                                sourceName=ASMUtil.getSourceInfo(pc.getConfig(),clazz,true).name;
200                                        }
201                                        catch (Throwable t) {
202                                                ExceptionUtil.rethrowIfNecessary(t);
203                                        }
204                                        
205                                        if(StringUtil.isEmpty(sourceName)) {
206                                                c=IOUtil.toString(children[i],(Charset)null);
207                                                int loc = c.indexOf("<clinit>");
208                                                if(loc !=-1) {
209                                                        c=c.substring(0,loc);
210                                                        c = ListUtil.last(c, "/\\",true).trim();
211                                                        if(StringUtil.endsWithIgnoreCase(c, ".cfc"))
212                                                                list.add(c);
213                                                }
214                                        }
215                                        else list.add(sourceName);
216                                        
217                                }
218                                if(list.size()>0) return list.toArray(new String[list.size()]);
219                        } 
220                }
221                return null;
222        }
223}