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.bytecode.util;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.util.HashMap;
024import java.util.Map;
025
026import lucee.commons.io.res.filter.ExtensionResourceFilter;
027import lucee.commons.lang.StringUtil;
028import lucee.runtime.PageContext;
029import lucee.runtime.config.Config;
030import lucee.runtime.functions.system.ExpandPath;
031import lucee.runtime.type.util.ListUtil;
032
033import org.objectweb.asm.ClassReader;
034import org.objectweb.asm.ClassVisitor;
035
036public class SourceNameClassVisitor extends ClassVisitor {
037
038
039
040        //private String sourceName;
041        //private String sourcePath;
042        private ExtensionResourceFilter filter;
043        private SourceInfo source;
044
045        public SourceNameClassVisitor(Config config, int arg0, boolean onlyCFC) {
046                super(arg0);
047                if(onlyCFC) {
048                        filter = new ExtensionResourceFilter(new String[]{config.getCFCExtension()},true,true);
049                }
050                else {
051                        filter = new ExtensionResourceFilter(config.getCFMLExtensions(),true,true);
052                        filter.addExtension(config.getCFCExtension());
053                }
054                
055        }
056
057        @Override
058        public void visitSource(String source, String debug) {
059                super.visitSource(source, debug);
060                if(!StringUtil.isEmpty(source)){
061                        
062                        String name=ListUtil.last(source, "/\\");
063                        
064                        if(filter.accept(name)) {
065                                // older than 4.2.1.008
066                                if(StringUtil.isEmpty(debug)) {
067                                        this.source=new SourceInfo(name,source); // source is a relative path
068                                }
069                                else {
070                                        //in that case source holds the absolute path
071                                        String[] arr=ListUtil.listToStringArray(debug, ';');
072                                        String str; int index;
073                                        Map<String,String> map=new HashMap<String, String>();
074                                        for(int i=0;i<arr.length;i++){
075                                                str=arr[i].trim();
076                                                index=str.indexOf(':');
077                                                if(index==-1) map.put(str.toLowerCase(),"");
078                                                else  map.put(str.substring(0,index).toLowerCase(),str.substring(index+1));
079                                                
080                                        }
081                                        String rel = map.get("rel");
082                                        String abs = map.get("abs");
083                                        if(StringUtil.isEmpty(abs)) abs=source;
084                                        
085                                        this.source=new SourceInfo(name,rel,abs);
086                                }
087                        }
088                }
089        }
090
091
092    public static SourceInfo getSourceInfo(Config config,Class clazz, boolean onlyCFC) throws IOException {
093                String name = "/"+clazz.getName().replace('.', '/')+".class";
094                InputStream in=clazz.getResourceAsStream(name);
095                ClassReader classReader=new ClassReader(in);
096                SourceNameClassVisitor visitor = new SourceNameClassVisitor(config,4,onlyCFC);
097                classReader.accept(visitor, 0);
098                if(visitor.source==null || visitor.source.name==null) return null;
099                return visitor.source;
100        
101    }
102
103        public static class SourceInfo {
104
105                public final String name;
106                public final  String relativePath;
107                private String _absolutePath;
108                
109                public SourceInfo(String name,String relativePath) {
110                        this(name,relativePath,null);
111                }
112                
113                public SourceInfo(String name,String relativePath,String absolutePath) {
114                        this.name=name;
115                        this.relativePath=relativePath;
116                        this._absolutePath=absolutePath;
117                }
118                
119                public String toString(){
120                        return new StringBuilder("absolute-path:"+_absolutePath+";relative-path:"+relativePath+";name:"+name).toString();
121                }
122
123                public String absolutePath(PageContext pc) {
124                        if(!StringUtil.isEmpty(_absolutePath)) return _absolutePath;
125                        try{
126                                _absolutePath=ExpandPath.call(pc, relativePath);
127                        }catch(Exception e) {}
128                        return _absolutePath;
129                }
130        }
131}