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 }