Package jfun.yan.xml

Source Code of jfun.yan.xml.Compiler

package jfun.yan.xml;

import java.beans.IntrospectionException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import jfun.util.dict.Dict;

import jfun.yan.ParameterBinder;
import jfun.yan.PropertyBinder;
import jfun.yan.util.resource.ResourceLoader;
import jfun.yan.util.resource.ResourceLoaders;

final class Compiler extends Constants{
  private static final Set reserved_tagnames = MyUtil.getNameSet(
      new String[]{SEQUENCE, LOCAL, BINDER, FUNCTION, CALLCC, EXPAND, MACRO}
  );
  private static final Set reserved_keys = MyUtil.getNameSet(
      new String[]{NULL}
  );
  private final Interpreter interpreter;
  private static final Set module_attributes =
    MyUtil.getNameSet(new String[]{NAME, DESCRIPTION, DEPENDS, EXPORT, HIDE});
  private static final Set body_attributes =
    MyUtil.getNameSet(new String[]{AUTOWIRE, SINGLETON, EAGER_INSTANTIATED, EAGER_INSTANTIATED2});
  private final String list_separator = ",";
  private final String map_separator = ",";

  Compiler(Interpreter interpreter) {
    this.interpreter = interpreter;
  }
  private Set getExportKeys(String export){
    final String[] export_names = NutsUtils.split(export, list_separator);
    final HashSet result = new HashSet();
    for(int i=0; i<export_names.length; i++){
      final String key = export_names[i];
      result.add(key);
    }
    return result;
  }
  //null is returned for wildcard
  private String[] compileFilterKeys(String keys, Location loc){
    keys = keys.trim();
    if(WILDCARD.equals(keys)){
      return null;
    }
    final String[] result = NutsUtils.split(keys, list_separator);
    final Set cache = new HashSet();
    for(int i=0; i<result.length; i++){
      final String name = result[i];
      if(cache.contains(name)){
        throw new ConfigurationException("<" + IMPORT + "> - duplicate name: "
            + name, loc);
      }
      if(WILDCARD.equals(name)){
        throw new ConfigurationException("<" + IMPORT + "> - wildcard cannot be combined with other keys",
            loc);
      }
      if(name.endsWith(WILDCARD)){
        final String name0 = name.substring(0, name.length()-1);
        checkIdentifier(name0, loc);
      }
      else{
        checkIdentifier(name, loc);
      }
      cache.add(name);
    }
    return result;
  }
  private Filter getExportFilter(String exports, String hidden, Location loc){
    if(exports==null){
      exports = "*";
    }
    return getKeyFilter(null, exports, hidden, loc);
  }
  private Filter getKeyFilter(String prefix, String includes,
      String excludes,
      Location loc){
    if(includes==null){
      //include nothing.
      return new Filter(new String[0],
          excludes==null?new String[0]: compileFilterKeys(excludes, loc));
    }
    includes = includes.trim();
    if(excludes==null){
      excludes = "";
    }
    return new Filter(compileFilterKeys(includes, loc), compileFilterKeys(excludes, loc));
  }
  private void checkImport(Import imp, IdChecker idchecker, Location loc){
    String prefix = imp.getPrefix();
    final String[] keys = imp.getKeys();
    for(int i=0; i<keys.length; i++){
      final String key = keys[i];
      idchecker.checkId(prefix+key, loc);
    }
  }
  private Module compileImportedModule(Tag tag)
  throws IOException, CyclicModuleDependencyException{
    final Location loc = tag.getLocation();
    final String resource = tag.getAttribute(RESOURCE);
    if(resource != null){
      MyUtil.assertAttributes(tag, MyUtil.getNameSet(new String[]{
          RESOURCE, CLASSPATH, NAMESPACE, INCLUDES, EXCLUDES
      }));
      final ClassLoader cloader = getClassLoader(tag);
      return interpreter.interpretResource(getResourceLoader(cloader), resource, null);
    }
    else{
      MyUtil.assertAttributes(tag, MyUtil.getNameSet(new String[]{
          FILE, NAMESPACE, INCLUDES, EXCLUDES
      }));
      final String filename = tag.getAttribute(FILE);
      if(filename==null)
        throw new ConfigurationException("either " + RESOURCE+ " or " + FILE
            +" has to be specified for <"
            + IMPORT + ">", loc);
      return interpreter.interpretFile(filename, loc);
    }
  }

  private Import compileImportTag(Tag tag, IdChecker checker)
  throws IOException, CyclicModuleDependencyException{
    final String includes_str = MyUtil.getMandatory(tag, INCLUDES);
    final String excludes_str = tag.getAttribute(EXCLUDES);
    final String namespace = tag.getAttribute(NAMESPACE);
    final String prefix = namespace==null?"":namespace+".";
    final Filter imports =  getKeyFilter(prefix, includes_str, excludes_str,
        tag.getLocation());
    final Module imported = compileImportedModule(tag);
    final Import ret =
      new Import(prefix, imports, imported, tag.getLocation());
    checkImport(ret, checker, tag.getLocation());
    return ret;
  }
  private static void checkIdentifier(String id, Location loc){
    if(reserved_keys.contains(id)){
      throw new ConfigurationException("\""+id+"\""
          + " is reserved.", loc);
    }
    if(!NutsUtils.isValidId(id)){
      throw new ConfigurationException("\""+id+"\""
          + " is not a valid id.", loc);
    }
  }
  private static final class GlobalIdChecker implements IdChecker{
    private final Location dep_loc;
    private final Set dependencies;
    private final HashMap imports = new HashMap();
    private boolean importing = true;
    GlobalIdChecker(String[] dependencies, Location loc){
      this.dependencies = MyUtil.getNameSet(dependencies);
      this.dep_loc = loc;
    }
    public void checkId(String id, Location loc){
      checkIdentifier(id, loc);
      if(dependencies.contains(id)){
        throw new ConfigurationException("\""+id+"\""
            + " duplicates with declared dependency name.", loc);
      }
      final Location duploc = (Location)imports.get(id);
      if(duploc!=null){
        throw new ConfigurationException("\""+id+"\"" +
            " duplicates with an imported name at line "
            + duploc.getLineNo(), loc);
      }
      if(importing)
        imports.put(id, loc);
    }
    void importDone(){
      importing = false;
    }
  }
  private static void checkMandatories(Tag tag, String[] names){
    for(int i=0; i<names.length; i++){
      final String name = names[i];
      MyUtil.getMandatory(tag, name);
    }
  }
  private void populateNutTag(Tag tag, Map nuts){
    final Location subloc = tag.getLocation();
    try{
      populateNut(nuts, tag);
    }
    catch(NoClassDefFoundError e){
      throw new ConfigurationException(e, subloc);
    }
    catch(ClassNotFoundException e){
      throw new ConfigurationException("nut class not found: "+e.getMessage(), subloc);
    }
    catch(IntrospectionException e){
      throw new ConfigurationException("invalid nut class: "+e.getMessage(), subloc);
    }
  }
  Module compileModule(Object id, Node node){
    if(node instanceof Tag){
      return compileModuleTag(id, (Tag)node);
    }
    else{
      throw new ConfigurationException("tag expected", node.getLocation());
    }
  }
  private static Dict seedImports(Dict ctxt, Import[] imports){
    final ArrayList keys = new ArrayList();
    final ArrayList stmts = new ArrayList();
    for(int i=0; i<imports.length; i++){
      final Import imp = imports[i];
      final String[] exports = imp.getKeys();
      final String prefix = imp.getPrefix();
      for(int j=0; j<exports.length; j++){
        final String key = prefix+exports[j];
        keys.add(key);
        stmts.add(new Bound(key, imp.getLocation()));
      }
    }
    return ctxt.puts(keys.toArray(), stmts.toArray());
  }
  Module compileModuleTag(Object id, Tag tag){
    final Location loc = tag.getLocation();
    if(!MODULE.equals(tag.getName())){
      throw new ConfigurationException("the top level tag has to be "
          +MODULE, loc);
    }
    MyUtil.assertAttributes(tag, module_attributes);
    checkMandatories(tag, new String[]{NAME});
    final String name = tag.getAttribute(NAME);
    final String desc = tag.getAttribute(DESCRIPTION);
    final String export_str = tag.getAttribute(EXPORT);
    final String hide_str = tag.getAttribute(HIDE);
    final Filter filter = getExportFilter(export_str, hide_str, loc);
    final StringPredicate exports = filter.getPredicate();//getExported(export);
    final String[] dependencies = getDependencies(tag.getAttribute(DEPENDS),
        tag.getLocation());
    final GlobalIdChecker idchecker = new GlobalIdChecker(dependencies,
        tag.getLocation());
    final List subnodes = tag.getSubNodes();
   
    final ArrayList importlist = new ArrayList();
    final HashMap nuts = new HashMap();
    final int nodecount = subnodes.size();
    int nodenum = 0;
    for(; nodenum<nodecount; nodenum++){
      final Node n = (Node)subnodes.get(nodenum);
      if(n instanceof Tag){
        final Tag t = (Tag)n;
        final String tagname = t.getName();
        if(IMPORT.equals(tagname)){
          try{
            importlist.add(compileImportTag(t, idchecker));
          }
          catch(IOException e){
            throw new ConfigurationException("import failed. "+e.getMessage(),
                t.getLocation());
          }
          catch(CyclicModuleDependencyException e){
            e.push(n.getLocation());
            throw e;
          }
          continue;
        }
        else if(NUT.equals(tagname)){
          populateNutTag(t, nuts);
          continue;
        }
      }
      break;
    }
    idchecker.importDone();
    //load standard nuts
    loadNuts(nuts);
   
    final Import[] imports = new Import[importlist.size()];
    importlist.toArray(imports);
    final Statements stmts =
      compileModuleStatements(id, tag, dependencies,
          imports, nodenum, nuts, idchecker);
    return new ModuleBuilder(id, name, desc,
        dependencies, imports, stmts, exports)
        .build(interpreter, interpreter.getFrame());
  }
 
  private void loadNuts(final HashMap nuts) {
    final Map external_nuts = interpreter.getExternalNuts();
    for(Iterator it=external_nuts.keySet().iterator(); it.hasNext();){
      final Object key = it.next();
      if(nuts.containsKey(key)) continue;
      nuts.put(key, external_nuts.get(key));
    }
    final String properties_file = "jfun/yan/xml/nuts/nuts.properties";
    try{
      loadNuts(getClass().getClassLoader(),
        properties_file, nuts);
    }
    catch(Exception e){
      e.printStackTrace();
      throw new IllegalStateException("could not load " + properties_file);
    }
  }
  private Statements compileModuleStatements(Object module_id, Tag tag,
      final String[] dependencies,
      final Import[] imports, int nodenum,
      final HashMap nuts, final GlobalIdChecker idchecker) {
    final List subnodes = tag.getSubNodes();
    final int nodecount = subnodes.size();
    if(nodenum < nodecount){
      final Node node = (Node)subnodes.get(nodenum);
      if(node instanceof Tag){
        final Tag t = (Tag)node;
        final String tagname = t.getName();
        if(BODY.equals(tagname)){
          final Statements body =
            compileBody(module_id, t, dependencies, imports, nuts,
                tag.getLocation(), idchecker);
          if(nodenum < nodecount-1){
            throw new ConfigurationException("<"+BODY+"> should be the last sub-element of <"+MODULE+">",
                t.getLocation());
          }
          return body;
        }
        else{
          throw new ConfigurationException("unknown tag <"+t.getName()+">",
              t.getLocation());
        }
      }
      else{
        throw new ConfigurationException("character data no supported.",
            node.getLocation());
      }
    }
    return new Statements(new String[0], new Stmt[0]);
  }
  private Statements compileBody(final Object module_id,
      final Tag tag, final String[] dependencies,
      final Import[] imports, final HashMap nuts, final Location loc, final GlobalIdChecker idchecker) {
    final Stmt[] deps = new Stmt[dependencies.length];
   
    for(int i=0; i<dependencies.length; i++){
      deps[i] = new Bound(dependencies[i], loc);
    }
    Dict ctxt = interpreter.getInitialCompileContext()
      .puts(dependencies, deps);
    ctxt = seedImports(ctxt, imports);
    return compileBody(module_id, nuts, tag, idchecker,
        ctxt);
  }
  private boolean parseEagerMode(Tag tag){
    final String eager_mode_name = MyUtil.getEagerMode(tag);
    if(eager_mode_name==null) return false;
    final Boolean result = NutsUtils.toBoolean(eager_mode_name);
    if(result==null)
      throw new ConfigurationException("Unrecognized eager init value: "+eager_mode_name,
          tag.getLocation());
    return result.booleanValue();
  }
  private Statements compileBody(
      Object module_id, Map nuts, Tag tag,
      IdChecker idchecker, Dict initial_ctxt){
    MyUtil.assertAttributes(tag, body_attributes);
   
    final String autowire = tag.getAttribute(AUTOWIRE);
    final Location loc = tag.getLocation();
    final ParameterBinder param_wiring = MyUtil.getParamWiring(autowire,
        interpreter.getCustomWiringModes(), loc,
        interpreter.getParameterWiring());
    final PropertyBinder prop_wiring = MyUtil.getPropWiring(autowire,
        interpreter.getCustomWiringModes(), loc,
        interpreter.getPropertyWiring());
    final SingletonMode singleton =
      MyUtil.getSingletonStrategy(tag.getAttribute(SINGLETON), loc,
          interpreter.getSingletonMode());
   
    final boolean default_eager_mode = parseEagerMode(tag);
    //final Runtime runtime = new Runtime(manager, cloader);
    final Statements stmts = new BodyCompiler(interpreter, module_id,
        //interpreter.getClassloader(),
        //interpreter.getLifecycleManager(),
        //interpreter.getBaseDir(),
        nuts, list_separator, map_separator,
        new WiringMode(param_wiring, prop_wiring, singleton),
        //interpreter.getCustomWiringModes(),
        //interpreter.getServices(),
        reserved_keys,
        default_eager_mode)
      .compileStatements(tag, initial_ctxt, idchecker,
          tag.getName(), tag.getSubNodes());
    return stmts;
  }
  private String[] getDependencies(String s, Location loc){
    if(s==null) return new String[0];
    final String[] keys = NutsUtils.split(s, list_separator);
    final HashSet buf = new HashSet(keys.length);
    for(int i=0; i<keys.length;i++){
      final String key = keys[i];
      if(reserved_keys.contains(key)){
        throw new ConfigurationException("dependency name reserved: "+key, loc);
      }
      if(buf.contains(key)){
        throw new ConfigurationException("duplicate dependency name: "+key, loc);
      }
      checkIdentifier(key, loc);
    }
    return keys;
  }
  private ClassLoader getClassLoader(Tag tag){
    final String classpath = tag.getAttribute(CLASSPATH);
    final ClassLoader cloader = getClass().getClassLoader();
    try{
      return NutsUtils.getClassLoader(cloader, classpath,
          interpreter.getBaseDir());
    }
    catch(MalformedURLException e){
      throw new ConfigurationException("invalid classpath",
          tag.getLocation());
    }
  }
  private void populateNut(Map nuts, Tag tag)
  throws ClassNotFoundException, IntrospectionException{
    final String nutname = MyUtil.getMandatory(tag, NAME);
    final String classname = MyUtil.getMandatory(tag, CLASS);
    final Location loc = tag.getLocation();
    if(reserved_tagnames.contains(nutname)){
      throw new ConfigurationException("nut name "+nutname+" is reserved, try a different name.",
          loc);
    }
    if(nuts.containsKey(nutname)){
      throw new ConfigurationException("nut name "+nutname+" is already used, try a different name.",
          loc);
    }
    //we use a separate class loader than the one used to load components.
    final ClassLoader cloader = getClassLoader(tag);
    final Class nutclass = cloader.loadClass(classname);
    nuts.put(nutname, interpreter.getIntrospector().getNutDescriptor(nutclass));
  }
  private StringPredicate getExported(String export){
    if(export==null || WILDCARD.equals(export.trim())){
      return StringPredicates.always();
    }
    else{
      final Set exports = getExportKeys(export);
      return StringPredicates.in(exports);
    }
  }
  private ResourceLoader getResourceLoader(ClassLoader cloader){
    return ResourceLoaders.or(cloader, interpreter.getResourceLoader());
  }
  private void loadNuts(ClassLoader loader,
      String resource, Map nuts)
  throws IOException, ClassNotFoundException, IntrospectionException{
    final ResourceLoader rloader = getResourceLoader(loader);
    final InputStream in = rloader.getResourceAsStream(resource);
    if(in == null)
      throw new IllegalStateException("could not find "
        + resource);
    try{
      final Properties props = new Properties();
      props.load(in);
      for(Iterator it=props.keySet().iterator(); it.hasNext();){
        final String key = (String)it.next();
        if(nuts.containsKey(key))
          continue;
        String classname = props.getProperty(key);
        if(classname.indexOf('.')<0){
          classname = "jfun.yan.xml.nuts."+classname;
        }
        final Class nutclass = loader.loadClass(classname);
        nuts.put(key, interpreter.getIntrospector().getNutDescriptor(nutclass));
      }
    }
    finally{
      in.close();
    }
  }
}
TOP

Related Classes of jfun.yan.xml.Compiler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.