001    package org.mesopotamia.lang.java;
002    
003    import java.io.IOException;
004    import java.io.Writer;
005    import java.util.ArrayList;
006    import java.util.Collection;
007    import java.util.HashMap;
008    import java.util.Iterator;
009    import java.util.List;
010    import java.util.Map;
011    import java.util.StringTokenizer;
012    
013    import org.mesopotamia.AstSourceUnit;
014    import org.mesopotamia.LanguageElement;
015    import org.mesopotamia.LanguageElementHandle;
016    import org.mesopotamia.MesopotamiaException;
017    import org.mesopotamia.MesopotamiaToken;
018    import org.mesopotamia.RepositoryLanguage;
019    import org.mesopotamia.Scan;
020    import org.mesopotamia.lang.java.ref.MethodInfo;
021    import org.mesopotamia.lang.java.ref.Scope;
022    import org.mesopotamia.lang.java.ref.SoftCachingScope;
023    import org.mesopotamia.lang.java.ref.TypeInfo;
024    import org.mesopotamia.lang.java.ref.VariableInfo;
025    import org.mesopotamia.lang.java.ref.bcel.JavaClassTypeInfoRepository;
026    
027    import biz.hammurapi.config.Context;
028    import biz.hammurapi.render.HtmlRenderer;
029    import biz.hammurapi.render.RenderingException;
030    import biz.hammurapi.util.Escaper;
031    
032    public class JavaSourceUnit extends AstSourceUnit implements Scope {
033            
034            /**
035             * Scan attribute of type ClassLoader for resolution of types. 
036             */
037            public static final String SCAN_CLASSLOADER = "SCAN_CLASSLOADER";
038    
039            /**
040             * Scan attribute of type ClassLoaderRepository for resolution of types.
041             */
042            public static final String SCAN_TYPE_INFO_REPOSITORY = "SCAN_TYPE_INFO_REPOSITORY";
043            
044            public JavaSourceUnit(
045                            org.mesopotamia.sql.SourceUnit dbData, 
046                            Scan scan,
047                            RepositoryLanguage repoLanguage, 
048                            Collection<Number> loadLevels)
049                            throws MesopotamiaException {
050                    
051                    super(dbData, scan, repoLanguage, loadLevels);
052                    synchronized (scan) {
053                            scanTypeInfoRepository = (JavaClassTypeInfoRepository) scan.getAttribute(SCAN_TYPE_INFO_REPOSITORY);
054                            if (scanTypeInfoRepository == null) {
055                                    Object scl = scan.getAttribute(SCAN_CLASSLOADER);
056                                    if (scl instanceof ClassLoader) {
057                                            scanTypeInfoRepository = new JavaClassTypeInfoRepository((ClassLoader) scl);
058                                    } else {
059                                            scanTypeInfoRepository = new JavaClassTypeInfoRepository(getClass().getClassLoader());
060                                    }
061                                    scan.setAttribute(SCAN_TYPE_INFO_REPOSITORY, scanTypeInfoRepository);
062                            }
063                    }
064            }
065            
066            private JavaClassTypeInfoRepository scanTypeInfoRepository;
067                    
068            public String getPackageName() {
069                    for (Object child: getLanguageElements()) {
070                            if (child instanceof PackageDefinition) {
071                                    return ((PackageDefinition) child).getPath();
072                            }
073                    }
074                    return "";
075            }
076            
077            private Scope cachingScope = new SoftCachingScope() {
078    
079                    @Override
080                    protected MethodInfo findMethodInternal(String name, String[] parameterTypes) {
081                            return JavaSourceUnit.this.findMethodInternal(name, parameterTypes);
082                    }
083    
084                    @Override
085                    protected TypeInfo findTypeInternal(String name) {
086                            return JavaSourceUnit.this.findTypeInternal(name);
087                    }
088    
089                    @Override
090                    protected VariableInfo findVariableInternal(String name) {
091                            return JavaSourceUnit.this.findVariableInternal(name);
092                    }
093                    
094            };
095            
096            public MethodInfo findMethod(String name, String[] parameterTypes) {
097                    return cachingScope.findMethod(name, parameterTypes);
098            }
099    
100            public TypeInfo findType(String name) {
101                    return cachingScope.findType(name);
102            }
103    
104            public VariableInfo findVariable(String name) {
105                    return cachingScope.findVariable(name);
106            }
107    
108            /**
109             * Finds method in static imports.
110             */
111            private MethodInfo findMethodInternal(String name, String[] parameterTypes) {
112                    for (Object le: getLanguageElements()) {
113                            if (le instanceof ImportDefinition) {
114                                    ImportDefinition iDef = (ImportDefinition) le;
115                                    String path = iDef.getPath();
116                                    if (iDef.isStatic()) {
117                                            TypeInfo staticImport = scanTypeInfoRepository.getTypeInfo(path, this);
118                                            if (staticImport!=null) {
119                                                    MethodInfo ret = staticImport.findTypeMethod(name, parameterTypes);
120                                                    if (ret!=null) {
121                                                            return ret;
122                                                    }
123                                            }
124                                    }
125                            }
126                    }
127                    return null;
128            }
129            
130            private static Collection<String> fcnVariants(String fcn) {
131                    StringTokenizer st = new StringTokenizer(fcn, ".");
132                    List<String> tokens = new ArrayList<String>();
133                    while (st.hasMoreTokens()) {
134                            tokens.add(st.nextToken());
135                    }
136                    
137                    Collection<String> ret = new ArrayList<String>();
138                    for (int i=0; i<tokens.size(); ++i) {
139                            StringBuffer sb = new StringBuffer();
140                            Iterator<String> sit = tokens.iterator();
141                            for (int j=tokens.size()-1; sit.hasNext(); --j) {
142                                    sb.append(sit.next());
143                                    if (sit.hasNext()) {
144                                            sb.append(j<=i ? "$" : ".");
145                                    }
146                            }
147                            ret.add(sb.toString());
148                    }
149                    return ret;             
150            }
151            
152            /**
153             * Finds type in defined and imported types.
154             */
155            private TypeInfo findTypeInternal(String name) {
156                    if (Null.NULL_FCN.equals(name)) {
157                            return Null.NULL_TYPE_INFO;
158                    }
159                    
160                    TypeInfo primitive = BuiltInType.getBuiltInType(name);
161                    if (primitive!=null) {
162                            return primitive;
163                    }
164                    
165                    synchronized (fcnMap) {
166                            LanguageElementHandle handle = fcnMap.get(name);
167                            if (handle!=null) {
168                                    return (TypeInfo) getScan().getLanguageElement(handle);
169                            }                       
170                    }
171                    
172                    int dotIdx = name.indexOf(".");
173                    if (dotIdx>0) { // FCN?
174                            TypeInfo ret = scanTypeInfoRepository.getTypeInfo(name, this);
175                            if (ret!=null) {
176                                    return ret;
177                            }
178                    }
179                    
180                    PackageDefinition pDef = null;
181                    for (Object le: getLanguageElements()) {
182                            if (le instanceof TypeDefinition) {
183                                    TypeDefinition td = (TypeDefinition) le;
184                                    if (name.equals(td.getName())) {
185                                            return td;
186                                    }
187                            } else if (le instanceof ImportDefinition) {
188                                    ImportDefinition iDef = (ImportDefinition) le;
189                                    String path = iDef.getPath();
190                                    if (iDef.isStatic()) {
191                                            TypeInfo staticImport = scanTypeInfoRepository.getTypeInfo(path, this);
192                                            if (staticImport!=null) {
193                                                    TypeInfo ret = staticImport.findNestedType(name);
194                                                    if (ret!=null) {
195                                                            return ret;
196                                                    }
197                                            }
198                                    } else {
199                                            if (dotIdx==-1 && path.endsWith("."+name)) {
200                                                    for (String variant: fcnVariants(path)) {
201                                                            TypeInfo ret = scanTypeInfoRepository.getTypeInfo(variant, this);
202                                                            if (this!=null) {
203                                                                    return ret;
204                                                            }
205                                                    }
206                                                    return null;
207                                            }
208                                            
209                                            if (dotIdx>0 && path.endsWith("."+name.substring(0, dotIdx))) {
210                                                    TypeInfo ret = scanTypeInfoRepository.getTypeInfo(path+"$"+name.substring(dotIdx+1), this);
211                                                    if (ret!=null) {
212                                                            return ret;
213                                                    }                                               
214                                            }
215                                            
216                                            if (path.endsWith("*")) {
217                                                    TypeInfo ret = scanTypeInfoRepository.getTypeInfo(path.substring(0, path.length()-1)+name, this);
218                                                    if (ret!=null) {
219                                                            return ret;
220                                                    }                                               
221                                            }
222                                    }
223                            } else if (le instanceof PackageDefinition) {
224                                    pDef = (PackageDefinition) le;
225                            }
226                    }
227                    
228                    TypeInfo ret = scanTypeInfoRepository.getTypeInfo("java.lang."+name, this);
229                    if (ret!=null) {
230                            return ret;
231                    }
232                    
233                    // Same package.
234                    if (pDef==null) {
235                            return scanTypeInfoRepository.getTypeInfo(name, this);                                                  
236                    } else {
237                            return scanTypeInfoRepository.getTypeInfo(pDef.getPath()+"."+name, this);                                                       
238                    }               
239            }
240            
241            /**
242             * Source unit doesn't define variables, but static imports do.
243             */
244            private VariableInfo findVariableInternal(String name) {
245                    for (Object le: getLanguageElements()) {
246                            if (le instanceof ImportDefinition) {
247                                    ImportDefinition iDef = (ImportDefinition) le;
248                                    String path = iDef.getPath();
249                                    if (iDef.isStatic()) {
250                                            TypeInfo staticImport = scanTypeInfoRepository.getTypeInfo(path, this);
251                                            if (staticImport!=null) {
252                                                    VariableInfo ret = staticImport.findTypeVariable(name);
253                                                    if (ret!=null) {
254                                                            return ret;
255                                                    }
256                                            }
257                                    }
258                            }
259                    }
260                    return null;
261            }
262            
263            private Map<String, LanguageElementHandle> fcnMap = new HashMap<String, LanguageElementHandle>();
264    
265            public void registerLocalOrAnonymous(String fcn, TypeDefinitionOrAnonymous typeDefinitionOrAnonymous) {
266                    synchronized (fcnMap) {
267                            fcnMap.put(fcn, ((LanguageElement) typeDefinitionOrAnonymous).getHandle());                     
268                    }
269            }
270            
271            @Override
272            public HtmlRenderer getRenderer(Context context) {
273                    return new HtmlRenderer() {
274    
275                            public void render(Writer out) throws IOException, RenderingException {
276                                    out.write("<PRE style=\"border: 1px solid gray\">");
277                                    Collection tokens = (Collection) getLevelData("token");
278                                    if (tokens!=null) {
279                                            java.util.Set<String> constants = new java.util.HashSet<String>();
280                                            constants.add("NUM_INT");
281                                            constants.add("CHAR_LITERAL");
282                                            constants.add("STRING_LITERAL");
283                                            constants.add("NUM_FLOAT");
284                                            constants.add("NUM_LONG");
285                                            constants.add("NUM_DOUBLE");
286                                            constants.add("HEX_DIGIT");
287                                            Iterator it = tokens.iterator();
288                                            int prevLine=-1;
289                                            while (it.hasNext()) {
290                                                    MesopotamiaToken token = (MesopotamiaToken) it.next();          
291                                                    if (token.getLine()!=prevLine) {
292                                                            if (prevLine!=-1) {
293                                                                    out.write("<BR/>");
294                                                            }
295                                                            out.write("<a name=\"line_"+token.getLine()+"\" class=\"lineNum\">"+token.getLine()+"</a>");
296                                                            prevLine=token.getLine();
297                                                    }
298                                                    if (token.getText()!=null && !"NEW_LINE".equals(token.getTypeName())) {
299                                                            if ("ML_COMMENT".equals(token.getTypeName())) {
300                                                                    StringTokenizer st = new StringTokenizer(token.getText(), "\n", true);
301                                                                    prevLine = token.getLine();
302                                                                    while (st.hasMoreTokens()) {
303                                                                            String nextToken = st.nextToken();
304                                                                            if ("\n".equals(nextToken)) {
305                                                                                    ++prevLine;
306                                                                                    out.write("<BR/><a name=\"line_"+prevLine+"\" class=\"lineNum\">"+prevLine+"</a>");                                   
307                                                                            } else {
308                                                                                    String cLine = ("!"+nextToken).trim().substring(1);
309                                                                                    out.write("<span class=\"comment\">"+Escaper.escapeHtml(cLine)+"</span>");
310                                                                            }
311                                                                    }
312                                                            } else if ("SL_COMMENT".equals(token.getTypeName())) {
313                                                                    out.write("<span class=\"comment\">"+("!"+Escaper.escapeHtml(token.getText())).trim().substring(1)+"</span>"); // Trim end.
314                                                            } else if (token.getTypeName().startsWith("LITERAL_")) {
315                                                                    out.write("<span class=\"reserved_word\"><B>"+Escaper.escapeHtml(token.getText())+"</B></span>");
316                                                            } else if (constants.contains(token.getTypeName())) {
317                                                                    out.write("<span class=\"literal\">"+Escaper.escapeHtml(token.getText())+"</span>");
318                                                            } else {
319                                                                    out.write(Escaper.escapeHtml(token.getText()));
320                                                            }
321                                                    }
322                                            }
323                                    }
324                                    out.write("</PRE>");
325                            }
326                            
327                    };
328            }
329            
330    }