1 package jfun.yan.xml; 2 3 import java.beans.IntrospectionException ; 4 import java.io.IOException ; 5 import java.io.InputStream ; 6 import java.net.MalformedURLException ; 7 8 import java.util.ArrayList ; 9 import java.util.HashMap ; 10 import java.util.HashSet ; 11 import java.util.Iterator ; 12 import java.util.List ; 13 import java.util.Map ; 14 import java.util.Properties ; 15 import java.util.Set ; 16 17 import jfun.util.dict.Dict; 18 19 import jfun.yan.ParameterBinder; 20 import jfun.yan.PropertyBinder; 21 import jfun.yan.util.resource.ResourceLoader; 22 import jfun.yan.util.resource.ResourceLoaders; 23 24 final class Compiler extends Constants{ 25 private static final Set reserved_tagnames = MyUtil.getNameSet( 26 new String []{SEQUENCE, LOCAL, BINDER, FUNCTION, CALLCC, EXPAND, MACRO} 27 ); 28 private static final Set reserved_keys = MyUtil.getNameSet( 29 new String []{NULL} 30 ); 31 private final Interpreter interpreter; 32 private static final Set module_attributes = 33 MyUtil.getNameSet(new String []{NAME, DESCRIPTION, DEPENDS, EXPORT, HIDE}); 34 private static final Set body_attributes = 35 MyUtil.getNameSet(new String []{AUTOWIRE, SINGLETON, EAGER_INSTANTIATED, EAGER_INSTANTIATED2}); 36 private final String list_separator = ","; 37 private final String map_separator = ","; 38 39 Compiler(Interpreter interpreter) { 40 this.interpreter = interpreter; 41 } 42 private Set getExportKeys(String export){ 43 final String [] export_names = NutsUtils.split(export, list_separator); 44 final HashSet result = new HashSet (); 45 for(int i=0; i<export_names.length; i++){ 46 final String key = export_names[i]; 47 result.add(key); 48 } 49 return result; 50 } 51 private String [] compileFilterKeys(String keys, Location loc){ 53 keys = keys.trim(); 54 if(WILDCARD.equals(keys)){ 55 return null; 56 } 57 final String [] result = NutsUtils.split(keys, list_separator); 58 final Set cache = new HashSet (); 59 for(int i=0; i<result.length; i++){ 60 final String name = result[i]; 61 if(cache.contains(name)){ 62 throw new ConfigurationException("<" + IMPORT + "> - duplicate name: " 63 + name, loc); 64 } 65 if(WILDCARD.equals(name)){ 66 throw new ConfigurationException("<" + IMPORT + "> - wildcard cannot be combined with other keys", 67 loc); 68 } 69 if(name.endsWith(WILDCARD)){ 70 final String name0 = name.substring(0, name.length()-1); 71 checkIdentifier(name0, loc); 72 } 73 else{ 74 checkIdentifier(name, loc); 75 } 76 cache.add(name); 77 } 78 return result; 79 } 80 private Filter getExportFilter(String exports, String hidden, Location loc){ 81 if(exports==null){ 82 exports = "*"; 83 } 84 return getKeyFilter(null, exports, hidden, loc); 85 } 86 private Filter getKeyFilter(String prefix, String includes, 87 String excludes, 88 Location loc){ 89 if(includes==null){ 90 return new Filter(new String [0], 92 excludes==null?new String [0]: compileFilterKeys(excludes, loc)); 93 } 94 includes = includes.trim(); 95 if(excludes==null){ 96 excludes = ""; 97 } 98 return new Filter(compileFilterKeys(includes, loc), compileFilterKeys(excludes, loc)); 99 } 100 private void checkImport(Import imp, IdChecker idchecker, Location loc){ 101 String prefix = imp.getPrefix(); 102 final String [] keys = imp.getKeys(); 103 for(int i=0; i<keys.length; i++){ 104 final String key = keys[i]; 105 idchecker.checkId(prefix+key, loc); 106 } 107 } 108 private Module compileImportedModule(Tag tag) 109 throws IOException , CyclicModuleDependencyException{ 110 final Location loc = tag.getLocation(); 111 final String resource = tag.getAttribute(RESOURCE); 112 if(resource != null){ 113 MyUtil.assertAttributes(tag, MyUtil.getNameSet(new String []{ 114 RESOURCE, CLASSPATH, NAMESPACE, INCLUDES, EXCLUDES 115 })); 116 final ClassLoader cloader = getClassLoader(tag); 117 return interpreter.interpretResource(getResourceLoader(cloader), resource, null); 118 } 119 else{ 120 MyUtil.assertAttributes(tag, MyUtil.getNameSet(new String []{ 121 FILE, NAMESPACE, INCLUDES, EXCLUDES 122 })); 123 final String filename = tag.getAttribute(FILE); 124 if(filename==null) 125 throw new ConfigurationException("either " + RESOURCE+ " or " + FILE 126 +" has to be specified for <" 127 + IMPORT + ">", loc); 128 return interpreter.interpretFile(filename, loc); 129 } 130 } 131 132 private Import compileImportTag(Tag tag, IdChecker checker) 133 throws IOException , CyclicModuleDependencyException{ 134 final String includes_str = MyUtil.getMandatory(tag, INCLUDES); 135 final String excludes_str = tag.getAttribute(EXCLUDES); 136 final String namespace = tag.getAttribute(NAMESPACE); 137 final String prefix = namespace==null?"":namespace+"."; 138 final Filter imports = getKeyFilter(prefix, includes_str, excludes_str, 139 tag.getLocation()); 140 final Module imported = compileImportedModule(tag); 141 final Import ret = 142 new Import(prefix, imports, imported, tag.getLocation()); 143 checkImport(ret, checker, tag.getLocation()); 144 return ret; 145 } 146 private static void checkIdentifier(String id, Location loc){ 147 if(reserved_keys.contains(id)){ 148 throw new ConfigurationException("\""+id+"\"" 149 + " is reserved.", loc); 150 } 151 if(!NutsUtils.isValidId(id)){ 152 throw new ConfigurationException("\""+id+"\"" 153 + " is not a valid id.", loc); 154 } 155 } 156 private static final class GlobalIdChecker implements IdChecker{ 157 private final Location dep_loc; 158 private final Set dependencies; 159 private final HashMap imports = new HashMap (); 160 private boolean importing = true; 161 GlobalIdChecker(String [] dependencies, Location loc){ 162 this.dependencies = MyUtil.getNameSet(dependencies); 163 this.dep_loc = loc; 164 } 165 public void checkId(String id, Location loc){ 166 checkIdentifier(id, loc); 167 if(dependencies.contains(id)){ 168 throw new ConfigurationException("\""+id+"\"" 169 + " duplicates with declared dependency name.", loc); 170 } 171 final Location duploc = (Location)imports.get(id); 172 if(duploc!=null){ 173 throw new ConfigurationException("\""+id+"\"" + 174 " duplicates with an imported name at line " 175 + duploc.getLineNo(), loc); 176 } 177 if(importing) 178 imports.put(id, loc); 179 } 180 void importDone(){ 181 importing = false; 182 } 183 } 184 private static void checkMandatories(Tag tag, String [] names){ 185 for(int i=0; i<names.length; i++){ 186 final String name = names[i]; 187 MyUtil.getMandatory(tag, name); 188 } 189 } 190 private void populateNutTag(Tag tag, Map nuts){ 191 final Location subloc = tag.getLocation(); 192 try{ 193 populateNut(nuts, tag); 194 } 195 catch(NoClassDefFoundError e){ 196 throw new ConfigurationException(e, subloc); 197 } 198 catch(ClassNotFoundException e){ 199 throw new ConfigurationException("nut class not found: "+e.getMessage(), subloc); 200 } 201 catch(IntrospectionException e){ 202 throw new ConfigurationException("invalid nut class: "+e.getMessage(), subloc); 203 } 204 } 205 Module compileModule(Object id, Node node){ 206 if(node instanceof Tag){ 207 return compileModuleTag(id, (Tag)node); 208 } 209 else{ 210 throw new ConfigurationException("tag expected", node.getLocation()); 211 } 212 } 213 private static Dict seedImports(Dict ctxt, Import[] imports){ 214 final ArrayList keys = new ArrayList (); 215 final ArrayList stmts = new ArrayList (); 216 for(int i=0; i<imports.length; i++){ 217 final Import imp = imports[i]; 218 final String [] exports = imp.getKeys(); 219 final String prefix = imp.getPrefix(); 220 for(int j=0; j<exports.length; j++){ 221 final String key = prefix+exports[j]; 222 keys.add(key); 223 stmts.add(new Bound(key, imp.getLocation())); 224 } 225 } 226 return ctxt.puts(keys.toArray(), stmts.toArray()); 227 } 228 Module compileModuleTag(Object id, Tag tag){ 229 final Location loc = tag.getLocation(); 230 if(!MODULE.equals(tag.getName())){ 231 throw new ConfigurationException("the top level tag has to be " 232 +MODULE, loc); 233 } 234 MyUtil.assertAttributes(tag, module_attributes); 235 checkMandatories(tag, new String []{NAME}); 236 final String name = tag.getAttribute(NAME); 237 final String desc = tag.getAttribute(DESCRIPTION); 238 final String export_str = tag.getAttribute(EXPORT); 239 final String hide_str = tag.getAttribute(HIDE); 240 final Filter filter = getExportFilter(export_str, hide_str, loc); 241 final StringPredicate exports = filter.getPredicate(); final String [] dependencies = getDependencies(tag.getAttribute(DEPENDS), 243 tag.getLocation()); 244 final GlobalIdChecker idchecker = new GlobalIdChecker(dependencies, 245 tag.getLocation()); 246 final List subnodes = tag.getSubNodes(); 247 248 final ArrayList importlist = new ArrayList (); 249 final HashMap nuts = new HashMap (); 250 final int nodecount = subnodes.size(); 251 int nodenum = 0; 252 for(; nodenum<nodecount; nodenum++){ 253 final Node n = (Node)subnodes.get(nodenum); 254 if(n instanceof Tag){ 255 final Tag t = (Tag)n; 256 final String tagname = t.getName(); 257 if(IMPORT.equals(tagname)){ 258 try{ 259 importlist.add(compileImportTag(t, idchecker)); 260 } 261 catch(IOException e){ 262 throw new ConfigurationException("import failed. "+e.getMessage(), 263 t.getLocation()); 264 } 265 catch(CyclicModuleDependencyException e){ 266 e.push(n.getLocation()); 267 throw e; 268 } 269 continue; 270 } 271 else if(NUT.equals(tagname)){ 272 populateNutTag(t, nuts); 273 continue; 274 } 275 } 276 break; 277 } 278 idchecker.importDone(); 279 loadNuts(nuts); 281 282 final Import[] imports = new Import[importlist.size()]; 283 importlist.toArray(imports); 284 final Statements stmts = 285 compileModuleStatements(id, tag, dependencies, 286 imports, nodenum, nuts, idchecker); 287 return new ModuleBuilder(id, name, desc, 288 dependencies, imports, stmts, exports) 289 .build(interpreter, interpreter.getFrame()); 290 } 291 292 private void loadNuts(final HashMap nuts) { 293 final Map external_nuts = interpreter.getExternalNuts(); 294 for(Iterator it=external_nuts.keySet().iterator(); it.hasNext();){ 295 final Object key = it.next(); 296 if(nuts.containsKey(key)) continue; 297 nuts.put(key, external_nuts.get(key)); 298 } 299 final String properties_file = "jfun/yan/xml/nuts/nuts.properties"; 300 try{ 301 loadNuts(getClass().getClassLoader(), 302 properties_file, nuts); 303 } 304 catch(Exception e){ 305 e.printStackTrace(); 306 throw new IllegalStateException ("could not load " + properties_file); 307 } 308 } 309 private Statements compileModuleStatements(Object module_id, Tag tag, 310 final String [] dependencies, 311 final Import[] imports, int nodenum, 312 final HashMap nuts, final GlobalIdChecker idchecker) { 313 final List subnodes = tag.getSubNodes(); 314 final int nodecount = subnodes.size(); 315 if(nodenum < nodecount){ 316 final Node node = (Node)subnodes.get(nodenum); 317 if(node instanceof Tag){ 318 final Tag t = (Tag)node; 319 final String tagname = t.getName(); 320 if(BODY.equals(tagname)){ 321 final Statements body = 322 compileBody(module_id, t, dependencies, imports, nuts, 323 tag.getLocation(), idchecker); 324 if(nodenum < nodecount-1){ 325 throw new ConfigurationException("<"+BODY+"> should be the last sub-element of <"+MODULE+">", 326 t.getLocation()); 327 } 328 return body; 329 } 330 else{ 331 throw new ConfigurationException("unknown tag <"+t.getName()+">", 332 t.getLocation()); 333 } 334 } 335 else{ 336 throw new ConfigurationException("character data no supported.", 337 node.getLocation()); 338 } 339 } 340 return new Statements(new String [0], new Stmt[0]); 341 } 342 private Statements compileBody(final Object module_id, 343 final Tag tag, final String [] dependencies, 344 final Import[] imports, final HashMap nuts, final Location loc, final GlobalIdChecker idchecker) { 345 final Stmt[] deps = new Stmt[dependencies.length]; 346 347 for(int i=0; i<dependencies.length; i++){ 348 deps[i] = new Bound(dependencies[i], loc); 349 } 350 Dict ctxt = interpreter.getInitialCompileContext() 351 .puts(dependencies, deps); 352 ctxt = seedImports(ctxt, imports); 353 return compileBody(module_id, nuts, tag, idchecker, 354 ctxt); 355 } 356 private boolean parseEagerMode(Tag tag){ 357 final String eager_mode_name = MyUtil.getEagerMode(tag); 358 if(eager_mode_name==null) return false; 359 final Boolean result = NutsUtils.toBoolean(eager_mode_name); 360 if(result==null) 361 throw new ConfigurationException("Unrecognized eager init value: "+eager_mode_name, 362 tag.getLocation()); 363 return result.booleanValue(); 364 } 365 private Statements compileBody( 366 Object module_id, Map nuts, Tag tag, 367 IdChecker idchecker, Dict initial_ctxt){ 368 MyUtil.assertAttributes(tag, body_attributes); 369 370 final String autowire = tag.getAttribute(AUTOWIRE); 371 final Location loc = tag.getLocation(); 372 final ParameterBinder param_wiring = MyUtil.getParamWiring(autowire, 373 interpreter.getCustomWiringModes(), loc, 374 interpreter.getParameterWiring()); 375 final PropertyBinder prop_wiring = MyUtil.getPropWiring(autowire, 376 interpreter.getCustomWiringModes(), loc, 377 interpreter.getPropertyWiring()); 378 final SingletonMode singleton = 379 MyUtil.getSingletonStrategy(tag.getAttribute(SINGLETON), loc, 380 interpreter.getSingletonMode()); 381 382 final boolean default_eager_mode = parseEagerMode(tag); 383 final Statements stmts = new BodyCompiler(interpreter, module_id, 385 nuts, list_separator, map_separator, 389 new WiringMode(param_wiring, prop_wiring, singleton), 390 reserved_keys, 393 default_eager_mode) 394 .compileStatements(tag, initial_ctxt, idchecker, 395 tag.getName(), tag.getSubNodes()); 396 return stmts; 397 } 398 private String [] getDependencies(String s, Location loc){ 399 if(s==null) return new String [0]; 400 final String [] keys = NutsUtils.split(s, list_separator); 401 final HashSet buf = new HashSet (keys.length); 402 for(int i=0; i<keys.length;i++){ 403 final String key = keys[i]; 404 if(reserved_keys.contains(key)){ 405 throw new ConfigurationException("dependency name reserved: "+key, loc); 406 } 407 if(buf.contains(key)){ 408 throw new ConfigurationException("duplicate dependency name: "+key, loc); 409 } 410 checkIdentifier(key, loc); 411 } 412 return keys; 413 } 414 private ClassLoader getClassLoader(Tag tag){ 415 final String classpath = tag.getAttribute(CLASSPATH); 416 final ClassLoader cloader = getClass().getClassLoader(); 417 try{ 418 return NutsUtils.getClassLoader(cloader, classpath, 419 interpreter.getBaseDir()); 420 } 421 catch(MalformedURLException e){ 422 throw new ConfigurationException("invalid classpath", 423 tag.getLocation()); 424 } 425 } 426 private void populateNut(Map nuts, Tag tag) 427 throws ClassNotFoundException , IntrospectionException { 428 final String nutname = MyUtil.getMandatory(tag, NAME); 429 final String classname = MyUtil.getMandatory(tag, CLASS); 430 final Location loc = tag.getLocation(); 431 if(reserved_tagnames.contains(nutname)){ 432 throw new ConfigurationException("nut name "+nutname+" is reserved, try a different name.", 433 loc); 434 } 435 if(nuts.containsKey(nutname)){ 436 throw new ConfigurationException("nut name "+nutname+" is already used, try a different name.", 437 loc); 438 } 439 final ClassLoader cloader = getClassLoader(tag); 441 final Class nutclass = cloader.loadClass(classname); 442 nuts.put(nutname, interpreter.getIntrospector().getNutDescriptor(nutclass)); 443 } 444 private StringPredicate getExported(String export){ 445 if(export==null || WILDCARD.equals(export.trim())){ 446 return StringPredicates.always(); 447 } 448 else{ 449 final Set exports = getExportKeys(export); 450 return StringPredicates.in(exports); 451 } 452 } 453 private ResourceLoader getResourceLoader(ClassLoader cloader){ 454 return ResourceLoaders.or(cloader, interpreter.getResourceLoader()); 455 } 456 private void loadNuts(ClassLoader loader, 457 String resource, Map nuts) 458 throws IOException , ClassNotFoundException , IntrospectionException { 459 final ResourceLoader rloader = getResourceLoader(loader); 460 final InputStream in = rloader.getResourceAsStream(resource); 461 if(in == null) 462 throw new IllegalStateException ("could not find " 463 + resource); 464 try{ 465 final Properties props = new Properties (); 466 props.load(in); 467 for(Iterator it=props.keySet().iterator(); it.hasNext();){ 468 final String key = (String )it.next(); 469 if(nuts.containsKey(key)) 470 continue; 471 String classname = props.getProperty(key); 472 if(classname.indexOf('.')<0){ 473 classname = "jfun.yan.xml.nuts."+classname; 474 } 475 final Class nutclass = loader.loadClass(classname); 476 nuts.put(key, interpreter.getIntrospector().getNutDescriptor(nutclass)); 477 } 478 } 479 finally{ 480 in.close(); 481 } 482 } 483 } 484 | Popular Tags |