Package ru.yandex.strictweb.scriptjava.compiler

Source Code of ru.yandex.strictweb.scriptjava.compiler.Parser

package ru.yandex.strictweb.scriptjava.compiler;

import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.persistence.Transient;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.TypeTags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;

import ru.yandex.strictweb.ajaxtools.annotation.AjaxTransient;
import ru.yandex.strictweb.ajaxtools.annotation.Presentable;
import ru.yandex.strictweb.ajaxtools.presentation.ClassMethodsInfo;
import ru.yandex.strictweb.scriptjava.base.ExtendsNative;
import ru.yandex.strictweb.scriptjava.base.Native;
import ru.yandex.strictweb.scriptjava.base.NativeCode;
import ru.yandex.strictweb.scriptjava.base.StrictWeb;
import ru.yandex.strictweb.scriptjava.plugins.EntityCompilerPlugin;

@SupportedSourceVersion(SourceVersion.RELEASE_5)
@SupportedAnnotationTypes("*")
public class Parser implements CompilerPlugin {
  public boolean obfuscate; 
  List<ParsedClass> classesList = new ArrayList<ParsedClass>();
  public Map<String, ParsedClass> classes = new TreeMap<String, ParsedClass>();
  public Vector<String> localVars = new Vector<String>();
  public Vector<ParsedClass> currentClass = new Vector<ParsedClass>();
  public VarType currentType;
  private Map<String, VarType> localVarsTypes = new TreeMap<String, VarType>();
//  private VarType variableDeclarationType;
  private int tempIndex;
  String indentPrefix = "\n";
  private int debugLevel;
  List<CompilerPlugin> plugins = new ArrayList<CompilerPlugin>();
  private AnonymDeclarationTester anonymousTester = new AnonymDeclarationTester();
  String lastFileName;
 
 
  public StringBuilder code = new StringBuilder();
 
  JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 
  private String currentLabel;
  private boolean parsingParameters;
  private boolean inlineStatement;
  private String superInvocation;
  private List<String> filesToParse = new ArrayList<String>();
  public Map<String, Set<String>> classLabels = new HashMap<String, Set<String>>();
  private List<String> currentImports;
  private Set<String> ignoredClasses = new HashSet<String>();
    private Set<String> warningMessages = new HashSet<String>();
 
  public Parser() {
    plugins.add(this);   
   
    addMathClass();
    addStringClass();
    addComparatorInterface();
    addDateClass();
    addRuntimeExceptionClass();
  }
 
  private void addComparatorInterface() {
    ParsedClass cl = new ParsedClass("Comparator", null);
    cl.isNative = true;
    cl.isInterface = true;
    classes.put(cl.name, cl);
    classesList.add(cl);
  }
 
  private void addStringClass() {
    ParsedClass cl = new ParsedClass("String", null);
    cl.isNative = true;
    classes.put(cl.name, cl);
    ParsedMethod m = new ParsedMethod("split", null, cl, VarType.ARRAY(VarType.STRING));
    cl.methods.put(m.name, m);

    m = new ParsedMethod("trim", null, cl, VarType.STRING);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("toLowerCase", null, cl, VarType.STRING);
    cl.methods.put(m.name, m);
   
    m = new ParsedMethod("toUpperCase", null, cl, VarType.STRING);
    cl.methods.put(m.name, m);
   
    m = new ParsedMethod("replace", null, cl, VarType.STRING);
    cl.methods.put(m.name, m);
   
    m = new ParsedMethod("substring", null, cl, VarType.STRING);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("charAt", null, cl, VarType.STRING);
    cl.methods.put(m.name, m);
   
    m = new ParsedMethod("indexOf", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
  }

  private void addMathClass() {
    ParsedClass cl = new ParsedClass("Math", null);
    cl.isNative = true;
    classes.put(cl.name, cl);
    ParsedMethod m = new ParsedMethod("round", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
    cl.staticMethods.add(m.name);
   
    m = new ParsedMethod("floor", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
    cl.staticMethods.add(m.name);
   
    m = new ParsedMethod("min", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
    cl.staticMethods.add(m.name);

    m = new ParsedMethod("max", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
    cl.staticMethods.add(m.name);
   
    m = new ParsedMethod("random", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
    cl.staticMethods.add(m.name);

    m = new ParsedMethod("sin", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
    cl.staticMethods.add(m.name);

    m = new ParsedMethod("cos", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
    cl.staticMethods.add(m.name);

    m = new ParsedMethod("abs", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
    cl.staticMethods.add(m.name);
  }
 
  private void addRuntimeExceptionClass() {
    ParsedClass cl = new ParsedClass("RuntimeException", null);
    cl.isNative = true;
    cl.isNative = true;
    classes.put(cl.name, cl);   
  }
 
  private void addDateClass() {
    ParsedClass cl = new ParsedClass("Date", null);
    cl.isNative = true;
    classes.put(cl.name, cl);
    ParsedMethod m = new ParsedMethod("getDate", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("setDate", null, cl, VarType.VOID);
    cl.methods.put(m.name, m);
   
    m = new ParsedMethod("setMonth", null, cl, VarType.VOID);
    cl.methods.put(m.name, m);
       
    m = new ParsedMethod("getMonth", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("setHours", null, cl, VarType.VOID);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("getHours", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("setMinutes", null, cl, VarType.VOID);
    cl.methods.put(m.name, m);
   
    m = new ParsedMethod("getMinutes", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("getSeconds", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("getTime", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("getDay", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);

    m = new ParsedMethod("toGMTString", null, cl, VarType.NUMBER);
    cl.methods.put(m.name, m);
  }
 
  public void setDebugLevel(int dl) {
    debugLevel = dl;
    if(dl<=0) indentPrefix = "";
  }
 
 
  static Pattern slashPattern = Pattern.compile("\\\\");
  static Pattern quotePattern = Pattern.compile("'");
  static Pattern nPattern = Pattern.compile("\n");
  static Pattern rPattern = Pattern.compile("\r");
 
  public static String safe(String s) {
    s = slashPattern.matcher(s).replaceAll("\\\\\\\\");
    s = quotePattern.matcher(s).replaceAll("\\\\'");
    s = nPattern.matcher(s).replaceAll("\\\\n");
    return '\''+rPattern.matcher(s).replaceAll("\\\\r")+'\'';
  }
 
  void parseEnum(Class<Enum> clas) throws Exception {
    String eName = clas.getSimpleName();
    ParsedClass cl = new ParsedClass(eName, null);
    cl.isEnum = true;
//    cl.skipInnerObfuscation = true;
    classes.put(eName, cl);
    VarType clType = new VarType(cl);
    ParsedMethod valuesMethod = new ParsedMethod("values", null, cl, VarType.ARRAY(cl.varType));
    cl.methods.put(valuesMethod.name, valuesMethod);
    cl.staticMethods.add(valuesMethod.name);
   
    ParsedMethod valueOf = new ParsedMethod("valueOf", null, cl, cl.varType);
    cl.methods.put(valueOf.name, valueOf);
    cl.staticMethods.add(valueOf.name);

    ParsedMethod toString = new ParsedMethod("toString", null, cl, VarType.STRING);
    cl.methods.put(toString.name, toString);
   
    ParsedMethod nameMethod = new ParsedMethod("toString", null, cl, VarType.STRING);
    cl.methods.put("name", nameMethod);

   
    code.append("function "+eName+"() {};");
        if(obfuscate) {
            code.append(getObfuscatedName(eName)+" = "+eName+";");           
        }
        code.append("\n"+eName+".values = [\n");
       
    boolean firstValue = true;

    for(Enum enumObj : clas.getEnumConstants()) {
      String enumName = enumObj.name();
      cl.fields.put(enumName, new ParsedField(enumName, clType, cl));
      cl.staticFields.add(enumName);
      //System.out.println(enumObj);
      if(!firstValue) code.append(",\n"); firstValue = false;
          if(obfuscate) {
              code.append(getObfuscatedName(eName)+"."+getObfuscatedName(enumName)+"=");           
          }     
      code.append(eName + "." + enumName + " = {");
      for(Method m : clas.getDeclaredMethods()) {
        if(m.getParameterTypes().length!=0) continue;
        if((m.getModifiers()&Modifier.PUBLIC)==0) continue;
        if((m.getModifiers()&Modifier.STATIC)!=0) continue;
        String mName = m.getName();
        if(mName.startsWith("get")) {
          mName = Character.toLowerCase(mName.charAt(3)) + mName.substring(4);
        }
        Object value = m.invoke(enumObj);
        ParsedMethod pm = new ParsedMethod(m.getName(), null, cl, drawEnumFieldOrMethod(mName, value));
        if(null != pm.retType) {
          cl.methods.put(m.getName(), pm);
        }
      }
       
      for(Field f : clas.getDeclaredFields()) {
        //System.out.println(f.toGenericString());
        if((f.getModifiers()&Modifier.PUBLIC)==0) continue;
        if((f.getModifiers()&Modifier.STATIC)!=0) continue;
        String fName = f.getName();
        VarType type = drawEnumFieldOrMethod(fName, f.get(enumObj));
        if(null!=type) {
          cl.fields.put(fName, new ParsedField(fName, type, cl));
        }
      }
       
      code.append("_isEnum:true, toString: function() {return '"+enumName+"';}}");
    }
    code.append("\n];\n"+eName+".valueOf = function(n){return "+eName+"[n];}\n");
  }

  private VarType drawEnumFieldOrMethod(String mName, Object value) {
    if(value == null) {
      //if(!first) code.append(", "); first = false;
      //code.append(mName + ": null");
      return null;   
    } else if(value instanceof String) {
      code.append(getObfuscatedName(mName) + ": " + safe(value.toString()) + ", ");
      return VarType.STRING;
    } else if(value instanceof Number) {
      code.append(getObfuscatedName(mName) + ": " + value.toString() + ", ");     
      return VarType.NUMBER;
    } else if(value.getClass().isEnum()) {
      String clName = value.getClass().getSimpleName();
      //System.out.println(clName);
      ParsedClass cl = classes.get(clName);
      if(null == cl) {
          printWarning(clName + " in enum does not exists");
          return null;
      }
      code.append(getObfuscatedName(mName) + ": " + clName+"."+((Enum)value).name() + ", ");
            return cl.varType;
    }
    return null;
  }
 
  private void printWarning(String message) {
      if(warningMessages.contains(message)) return;
      warningMessages.add(message);
        System.err.println("Warning: " + message);
    }

    public static String operatorName(int i)
    {
        switch(i)
        {
        case 46: // '.'
            return "+";

        case 47: // '/'
            return "-";

        case 48: // '0'
            return "!";

        case 49: // '1'
            return "~";

        case 50: // '2'
            return "++";

        case 51: // '3'
            return "--";

        case 52: // '4'
            return "++";

        case 53: // '5'
            return "--";

        case 54: // '6'
            return "<*nullchk*>";

        case 55: // '7'
            return "||";

        case 56: // '8'
            return "&&";

        case 60: // '<'
            return "==";

        case 61: // '='
            return "!=";

        case 62: // '>'
            return "<";

        case 63: // '?'
            return ">";

        case 64: // '@'
            return "<=";

        case 65: // 'A'
            return ">=";

        case 57: // '9'
            return "|";

        case 58: // ':'
            return "^";

        case 59: // ';'
            return "&";

        case 66: // 'B'
            return "<<";

        case 67: // 'C'
            return ">>";

        case 68: // 'D'
            return ">>>";

        case 69: // 'E'
            return "+";

        case 70: // 'F'
            return "-";

        case 71: // 'G'
            return "*";

        case 72: // 'H'
            return "/";

        case 73: // 'I'
            return "%";
        }
        throw new RuntimeException();
    }

  public void parseFile(String f){
//    System.out.println(f);

    filesToParse.add(f);
  }
 
  static class MyFileObject extends SimpleJavaFileObject {
    SoftReference<StringBuilder> content = null;
   
    protected MyFileObject(URI uri, javax.tools.JavaFileObject.Kind kind) {
      super(uri, kind);
    }
   
    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
      if(content==null || content.get() == null) {
        StringBuilder builder = new StringBuilder();
        content = new SoftReference<StringBuilder>(builder);
        Reader r = null;
        try {
          if("classpath".equals(uri.getScheme())) {
            r = new InputStreamReader(this.getClass().getResourceAsStream(uri.getPath()));
          } else {
            r = new FileReader(uri.getPath());
          }
          char[] cbuf = new char[4096];
          int len = 0;
          while((len=r.read(cbuf)) > 0) builder.append(cbuf, 0, len);
        }finally {
          if(r!=null) r.close();
        }
      }
     
      return content.get();
    }
  }
 
  void parseAllFiles() throws Exception {
   
    compiler = ToolProvider.getSystemJavaCompiler();
   
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(
      null, Locale.getDefault(), Charset.forName("utf8"));

    List<JavaFileObject> units = new ArrayList<JavaFileObject>();
    for(String f: filesToParse) {
      units.add(new MyFileObject(new URI(f), javax.tools.JavaFileObject.Kind.SOURCE));
    }
   
    JavacTaskImpl task = (JavacTaskImpl)compiler.getTask(null, fileManager,
      new DiagnosticListener<JavaFileObject>() {
        @Override
        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
//          System.err.println(diagnostic);
          if(diagnostic.getKind() == Kind.ERROR
            ) {
            throw new RuntimeException(diagnostic.toString());
          }
        }
      },
      null, null, units);

    for(Tree t : task.parse()) {
      currentImports = new ArrayList<String>();
      parse(t);       
    }

    fileManager.close();
    filesToParse.clear();
  }
 
  public void parse(Object node) {
    if(null == node) {
      code.append("null");
      return;
    }
   
//    if(!(node instanceof ASTNode)) {
//      System.out.println(node);
//    }
   
    String typeName = node.getClass().getCanonicalName();
    String name = "parse" + node.getClass().getCanonicalName().replace(
      node.getClass().getPackage().getName()+".", "").replace('.', '_');
   
    try {
      //System.out.println(name);
      Method method = getClass().getMethod(name, node.getClass());
      method.setAccessible(true);
      method.invoke(this, node);
    } catch(NoSuchMethodException e) {
      throw new RuntimeException("Cant find method:\n\npublic void " + name +"(" + typeName + " node) {\n\tSystem.out.println(node);\n\tthrow new RuntimeException(\"FIX ME\");\n}\n");
    } catch(Exception e) {
      if(e.getCause()!=null && e.getCause() instanceof RuntimeException) {
        throw (RuntimeException)e.getCause();
      }
      throw new RuntimeException(e);
    }
  }
 
  public void parseList(List list, String separator) {
    currentType = null;
    boolean first = true;
    for(Object o : list) {
      if(!first) code.append(separator);
      first = false;
      parse(o);
    }
    currentType = null;
  }
 

  public void parseArguments(String open, List arguments, String close) {
    code.append(open);
    for(int i=0; i<arguments.size(); i++) {
      if(i>0) code.append(debugLevel == 0 ? "," : ", ");
      parse(arguments.get(i));
    }
    code.append(close);
  }
 
  public static boolean hasAnnotation(String an, JCModifiers modifiers) {
    if(modifiers == null || modifiers.getAnnotations() == null) return false;
    for(JCAnnotation a : modifiers.getAnnotations()) {
//      System.out.println(a.annotationType);
      if(a.annotationType.toString().equals(an)) return true;
    }
    return false;
  }

  public boolean isAbstract(JCModifiers modifiers) {
    return (modifiers.flags&Flags.ABSTRACT) != 0;
  }

  public boolean isStatic(JCModifiers modifiers) {
    return (modifiers.flags&Flags.STATIC) != 0;
  }
 
  public boolean isFinal(JCModifiers modifiers) {
    return (modifiers.flags&Flags.FINAL) != 0;
  }
 
  public boolean isInterface(JCClassDecl classDecl) {
    return (classDecl.getModifiers().flags&Flags.INTERFACE) != 0;
  }

  public boolean isConstructor(JCMethodDecl m) {
    return m.getReturnType()==null;
  }

  public String getAnnotationValue(String an, JCModifiers modifiers) {
    for(JCAnnotation a : modifiers.getAnnotations()) {
      if(a.annotationType.toString().equals(an)) {
        if(a.args.size() != 1) throw new RuntimeException("Annotation value args!=1");
        JCExpression value = a.args.get(0);
        String val = "";
        try {
            val = value.getClass().getField("value").get(value).toString();
        }catch(Throwable e) {new RuntimeException(e);}
       
//        System.out.println("getAnnotationValue: " + val);
       
//        if(val.indexOf("(") <0) return "";
//        val = val.replaceFirst("^[^\\(]+\\(", ""); // убираем то, что до скобочки
//        val = val.replaceFirst("\\)$", ""); //убираем последную скобочку
//        val = val.replaceAll("\"\\s*\\+\\s*\"", ""); // убираем " + "
//        val = val.replaceAll("\\\\\"", "\"");
//        val = val.replaceAll("\\\\'", "'");
//        val = val.replaceAll("([^\\\\])\\\\\\\\", "$1\\\\");
////        System.out.println("getAnnotationValue: " + val);
//        //throw new RuntimeException(val);
//        if(val.startsWith("\"") && val.endsWith("\""))
//          return val.substring(1, val.length()-1);
        return val;
      }
    }
   
    throw new RuntimeException("Cant find Annotation " + an);
  }

  Class<?> getClassSafe(String typeName) {
    try {
      return Class.forName(typeName);
    }catch(Throwable th) {       
          try {
              return Class.forName(typeName.replaceFirst("\\.([^\\.]+)$", "\\$$1"));
          }catch(Throwable th1) {         
              return null;
          }      
    }   
  }
 
  public VarType getMethodType(VarType type, String n) throws NoSuchMethodException {
    String typeName = type.getName();
    ParsedClass cl = getParsedClassByName(typeName);
   
    if(null == cl) {
//        System.out.println("Unknown method: " + typeName+"."+n);
      throw new NoSuchMethodException("Unknown method: " + typeName+"."+n);
    }
   
    ParsedMethod m = cl.methods.get(n);
    //if(null==m && n.equals("toString")) return VarType.STRING;
    if(null == m) throw new NoSuchMethodException("Unknown method: " + typeName+"."+n);
    return m.retType.implement(cl.parameters, currentType);
  }

  public ParsedClass getParsedClassByName(String typeName) throws NoSuchMethodException {
    ParsedClass cl = classes.get(typeName);
    if(null == cl) {
//      System.out.println("Try find class in imports: " + typeName);
      Class<?> claz = findClassInImports(typeName);
//      System.out.println("result: " + claz);
      if(null == claz) throw new NoSuchMethodException("Unknown class: " + typeName);

      cl = tryParseBean(claz);
    }
    return cl;
  }

 
  private ParsedClass tryParseBean(Class<?> claz) {
    if(!ClassMethodsInfo.isPojoBean(claz)) {
//        System.out.println(claz.getName() + " IS NOT POJO!!");
      return null;
    }

        return parseAsPOJOBean(claz);
  }

    public ParsedClass parseAsPOJOBean(Class<?> claz) {
        ParsedClass cl = new ParsedClass(claz.getSimpleName(), null);
        addClassLabel(claz, EntityCompilerPlugin.LABEL);

        cl.canCreateNewInstance = false;
        cl.skipInnerObfuscation = true;
        classes.put(cl.name, cl);

        for(Method m : claz.getMethods()) {
            if(ClassMethodsInfo.isSetter(m)) {
                ParsedMethod pm = new ParsedMethod(m.getName(), null, cl, VarType.VOID);
                cl.methods.put(pm.name, pm);
            }

            if(ClassMethodsInfo.isGetter(m)
                && !ClassMethodsInfo.excludedMethods.contains(m)
                && !m.isAnnotationPresent(AjaxTransient.class)
                && (!m.isAnnotationPresent(Transient.class) || m.isAnnotationPresent(Presentable.class))) {

                try {
                    ParsedMethod pm = new ParsedMethod(m.getName(), null, cl, new VarType(m.getGenericReturnType()));
                    cl.methods.put(pm.name, pm);
                }catch(Throwable th) {
                    System.err.println("Warning: " + th.getMessage());
                }
            }
        }

        for(Field f : claz.getFields())
            if(!f.isAnnotationPresent(AjaxTransient.class)
                && (!f.isAnnotationPresent(Transient.class) || f.isAnnotationPresent(Presentable.class))) {

            ParsedField pf = new ParsedField(f.getName(), VarType.STRING, cl);
            pf.type = new VarType(f.getGenericType());
            cl.fields.put(pf.name, pf);

        }

        printWarning(claz + " parsed as POJO bean");

        return cl;
    }

    private Class<?> findClassInImports(String typeName) {
    Class<?> cl = getClassSafe("java.lang." + typeName);
    if(cl != null) return cl;
   
    String suffix = "." + typeName;
    for(String fcn : currentImports) {
      if(fcn.endsWith(suffix)) {
        cl = getClassSafe(fcn);
        if(cl != null) return cl;
      }
     
      if(fcn.endsWith(".*")) {
        cl = getClassSafe(fcn.substring(0, fcn.length()-2) + suffix);
        if(cl != null) return cl;       
      }
    }
   
    return null;
  }

  public void compile() throws Exception {
    parseAllFiles();
   
    for(int i=0; i<classesList.size(); i++) {
      ParsedClass cl = classesList.get(i);
      currentImports = cl.importList;
      //System.out.println(cl.name + " :: " + cl.compiled + " :: " + cl.isNative);
      if(cl.compiled) continue;
      localVars.clear();
      indentPrefix = ifObfuscated("", "\n");
      if(cl.isNative || null == cl.type/* || cl.type.isInterface()*/) continue;     

//      System.out.println(cl.name);

      currentClass.add(cl);
//      lastSource = cl.sourceCode;
      lastFileName = cl.fileName;
      for(int j=plugins.size()-1;j>=0;j--) {
        if(plugins.get(j).compileClassFieldsAndMethods(cl)) break;
      }
      currentClass.setSize(currentClass.size() - 1);
    }
   
    for(int i=0; i<classesList.size(); i++) {
      ParsedClass cl = classesList.get(i);
      currentImports = cl.importList;
      if(cl.compiled) continue;
      cl.compiled = true;
      localVars.clear();
      currentClass.add(cl);
//      lastSource = cl.sourceCode;
      lastFileName = cl.fileName;
      for(int j=plugins.size()-1;j>=0;j--) {
//        System.out.println(plugins.get(j).getClass() + " :: " + cl.name);
        if(plugins.get(j).compileClassInitializers(cl)) break;
      }
      //cl.compileInitializers(this);
      currentClass.setSize(currentClass.size() - 1);
    }
  }
 
  public void parseParameters(String open, List<JCVariableDecl> parameters, String close) {
    parsingParameters = true;
    code.append(open);
    for(int i=0; i<parameters.size(); i++) {
      if(i>0) code.append(debugLevel==0?",":", ");
      parse(parameters.get(i));
    }
    code.append(close);
    parsingParameters = false;
  }

  Pattern ajaxNamePattern = Pattern.compile("([^\\.]+)$");
    private ParsedMethod currentParsedMethod;
 
    private boolean checkForAjaxNameMagicMethod(String mName, ParsedClass cl, StringBuilder code, List<JCExpression> arguments) {
        if (!mName.equals("ajaxName") || !cl.methods.get(mName).cl.name.equals(StrictWeb.class.getSimpleName()) || arguments.size() != 1) {
            return false;
        }

        while ('.' == code.charAt(code.length() - 1) || Character.isJavaIdentifierPart(code.charAt(code.length() - 1))) {
            code.setLength(code.length() - 1);
        }
        String m = arguments.get(0).toString();
        if (m.endsWith(".class")) {
            m = m.replace(".class", "");
        } else {

            Matcher matcher = ajaxNamePattern.matcher(m);
            if (!matcher.find())
                throw new RuntimeException("не могу понять что делать");

            m = matcher.group(1);

            if (m.contains("(")) {
                m = m.replaceFirst("\\(.*$", "");
                if(m.matches("^[gs]et[A-Z].*$")) {
                    // setters and getters
                    m = Character.toLowerCase(m.charAt(3)) + m.substring(4);
                }
            }
        }
        code.append('"' + m + '"');
        currentType = VarType.STRING;
        return true;
    }
    
  public boolean invokeMethod(String mName, JCMethodInvocation inv, List arguments) {

    try {
//        System.out.println(currentType.getName() + " :: " + mName);
       
      VarType retType = getMethodType(currentType, mName);
      ParsedClass cl = classes.get(currentType.getName());
     
      if(checkForAjaxNameMagicMethod(mName, cl, code, arguments)) return true;
     
      ParsedMethod parsedMethod = cl.methods.get(mName);
      if(null != parsedMethod) {
          parsedMethod.invokeCounter.inc(currentParsedMethod);
      }
     
      if(null!=cl && cl.isEnum) {
        if(mName.startsWith("get") || mName.startsWith("is")) {
          mName = Character.toLowerCase(mName.charAt(3)) + mName.substring(4);
        }
                mName = parsedMethod.name;
        code.append(mName);
        if(mName.equals("valueOf") || mName.equals("toString")) {
          parseArguments("(", arguments, ")");           
        }
      } else {
        if(null!=superInvocation) {
          code.append(getObfuscatedName(superInvocation+mName));
          superInvocation = null;
        } else {
            code.append(getObfuscatedName(mName, cl.isNative || cl.skipInnerObfuscation));
        }
        //System.out.println(inv);
        parseArguments("(", arguments, ")");
      }
      currentType = retType;
      //System.out.println(cl.name+"."+mName+" :: "+retType);

    }catch(NoSuchMethodException re) {
      String spInvName = "specialInvocationMethod$_"+mName;
      try {
        Method spInvMethod = this.getClass().getMethod(spInvName, JCMethodInvocation.class);
        spInvMethod.invoke(this, inv);
      }catch(Exception re2) {
        String spInvMethodName = "specialInvocationMethod" + currentType.getName()+"_"+mName;
//          re2.printStackTrace();
        throwCompileError(inv, re2.getMessage() + "\n\n" +
            "Try to add method:\n\n" +
            "public void " + spInvMethodName +"(JCMethodInvocation inv) {\n" +
              "\tthrow new RuntimeException(\"FIX ME\");\n" +
            "}\n" +
            "  OR\n\n" +
            "public void " + spInvName +"(JCMethodInvocation inv) {\n" +
              "\tthrow new RuntimeException(\"FIX ME\");\n" +
            "}\n"
            );
      }
    }
   
    return true;
  }
 
  public void specialInvocationMethodCollection_size(JCMethodInvocation inv) {
    specialInvocationMethodList_size(inv);
  }

  public void specialInvocationMethodDate_getYear(JCMethodInvocation inv) {
    code.append("getFullYear()");
    currentType = VarType.NUMBER; 
  }

  public void specialInvocationMethodDate_setYear(JCMethodInvocation inv) {
    code.append("setFullYear");
    parseArguments("(", inv.getArguments(), ")");
    currentType = null;   
  }

  public void specialInvocationMethod$_toString(JCMethodInvocation inv) {
    code.append("toString");
    parseArguments("(", inv.getArguments(), ")");
    currentType = VarType.STRING;
  }

  public void specialInvocationMethodLinkedList_pop(JCMethodInvocation inv) {
    if(currentType.parametersSize() != 1) throw new RuntimeException("LinkedList.parameters.size() != 1");
    VarType t = currentType.parametersGet(0);
    code.append("pop()");
    currentType = t;
  }

  public void specialInvocationMethodLinkedList_poll(JCMethodInvocation inv) {
    if(currentType.parametersSize() != 1) throw new RuntimeException("LinkedList.parameters.size() != 1");
    VarType t = currentType.parametersGet(0);
    code.append("shift()");
    currentType = t;
  }

  public void specialInvocationMethodLinkedList_push(JCMethodInvocation inv) {
    specialInvocationMethodList_add(inv);
  }

  public void specialInvocationMethodLinkedList_size(JCMethodInvocation inv) {
    specialInvocationMethodList_size(inv);
  }

  public void specialInvocationMethodList_add(JCMethodInvocation inv) {
    code.append("push");
    parseArguments("(", inv.getArguments(), ")");
    currentType = null;
  }

  public void specialInvocationMethodVector_add(JCMethodInvocation inv) {
    code.append("push");
    parseArguments("(", inv.getArguments(), ")");
    currentType = null;
  }

  public void specialInvocationMethodList_clear(JCMethodInvocation inv) {
    code.append("length=0");
    currentType = VarType.VOID;
  }

  public void specialInvocationMethodList_get(JCMethodInvocation inv) {
    if(currentType.parametersSize() != 1) throw new RuntimeException("List.parameters.size() != 1");
    VarType t = currentType.parametersGet(0);
    removeLastPoint();
    parseArguments("[", inv.getArguments(), "]");
    currentType = t;
  }

  public void specialInvocationMethodList_size(JCMethodInvocation inv) {
    code.append("length");
    currentType = VarType.NUMBER;
  }

  public void specialInvocationMethodMap_get(JCMethodInvocation inv) {
    if(currentType.parametersSize() != 2) throw new RuntimeException("Map.parameters.size() != 2");
    VarType t = currentType.parametersGet(1);
    removeLastPoint();
    parseArguments("[", inv.getArguments(), "]");
    currentType = t;
    //System.out.println("Map.get :: "+t);   
  }

  public void specialInvocationMethodMap_keySet(JCMethodInvocation inv) {
    removeLastPoint();
    currentType = new VarType(currentType);
    currentType.setName("Set");
  }

  public void specialInvocationMethodMap_put(JCMethodInvocation inv) {
    if(inv.getArguments().size() != 2) throw new RuntimeException("Map.put().arguments().size() != 2");
    removeLastPoint();
    code.append("[");
    parse(inv.getArguments().get(0));
    code.append("]=(");
    parse(inv.getArguments().get(1));
    code.append(")");
    currentType = null;
  }
 
  public void specialInvocationMethodList_set(JCMethodInvocation inv) {
    if(inv.getArguments().size() != 2) throw new RuntimeException("Map.put().arguments().size() != 2");
    removeLastPoint();
    code.append("[");
    parse(inv.getArguments().get(0));
    code.append("]=(");
    parse(inv.getArguments().get(1));
    code.append(")");
    currentType = null;
  }

  public void specialInvocationMethodMath_rint(JCMethodInvocation inv) {
    throw new RuntimeException("Use StrictWeb.round2");
//    code.append("round2");
//    parseArguments("(", inv.getArguments(), ")");
//    currentType = VarType.NUMBER;
  }
 
  public void specialInvocationMethodSet_remove(JCMethodInvocation inv) {
      throw new RuntimeException("Use StrictWeb.jsDelObjectProperty");
  }

  public void specialInvocationMethodSet_add(JCMethodInvocation inv) {
    removeLastPoint();
    parseArguments("[", inv.getArguments(), "]");
    code.append("=1");
    currentType = null;
  }
 
  public void specialInvocationMethodTreeSet_add(JCMethodInvocation inv) {
      specialInvocationMethodSet_add(inv);
  }

  public void specialInvocationMethodSet_contains(JCMethodInvocation inv) {
    if(currentType.parametersSize() != 1) throw new RuntimeException("Set.parameters.size() != 1");
    VarType t = currentType.parametersGet(0);
    removeLastPoint();
    parseArguments("[", inv.getArguments(), "]");
    currentType = t;
  }

  public void specialInvocationMethodTreeSet_contains(JCMethodInvocation inv) {
      specialInvocationMethodSet_contains(inv);
 
 
  public void specialInvocationMethodString_length(JCMethodInvocation inv) {
    specialInvocationMethodList_size(inv);
  }

  public void specialInvocationMethodString_replaceAll(JCMethodInvocation inv) {
    code.append("replace(new RegExp(");
    parse(inv.getArguments().get(0));
    code.append(", 'g'), ");
    parse(inv.getArguments().get(1));
    code.append(")");
    currentType = VarType.STRING;
  }
 
  public void specialInvocationMethodString_matches(JCMethodInvocation inv) {
    code.append("match(new RegExp(");
    parse(inv.getArguments().get(0));
    code.append("))");
    currentType = VarType.STRING;
  }

  public void specialInvocationMethodString_replaceFirst(JCMethodInvocation inv) {
   
    code.append("replace(new RegExp(");
    parse(inv.getArguments().get(0));
    code.append(", ''), ");
    parse(inv.getArguments().get(1));
    code.append(")");
    currentType = VarType.STRING;
  }
 
  public void removeLastPoint() {
    while('.'==code.charAt(code.length()-1)) {
      code.setLength(code.length() - 1);
    }
  }
 
  private int getTempIndex() {
    return tempIndex ++;
  }

  public void specialInstanceCreationDate(JCNewClass node) {
    VarType type = new VarType(node.clazz);
    code.append("new "+type.getName());
    parseArguments("(", node.getArguments(), ")");
    currentType = type;
  }

  public void specialInstanceCreationLinkedList(JCNewClass node) {
    specialInstanceCreationVector(node);
  }

  public void specialInstanceCreationTreeMap(JCNewClass node) {
    code.append("{}");
    currentType = new VarType(node.clazz);
  }
 
  public void specialInstanceCreationHashSet(JCNewClass node) {
    code.append("{}");
    currentType = new VarType(node.clazz);
  }

  public void specialInstanceCreationHashMap(JCNewClass node) {
    code.append("{}");
    currentType = new VarType(node.clazz);
  }

  public void specialInstanceCreationTreeSet(JCNewClass node) {
    code.append("{}");
    currentType = new VarType(node.clazz);
  }

  public void specialInstanceCreationVector(JCNewClass node) {
    code.append("[]");
    currentType = new VarType(node.clazz);
  }
 
  public void specialInstanceCreationArrayList(JCNewClass node) {
    code.append("[]");
    currentType = new VarType(node.clazz);
  }
 
  private ParsedClass createNewParsedClass(String name, JCTree superclassType, JCModifiers modifiers, List bodies, JCClassDecl typeDecl) {

    if(classes.get(name) != null) throw new RuntimeException("Class already parsed: " + name);
    ParsedClass cl = new ParsedClass(name, typeDecl);
    cl.importList = currentImports;
    cl.isInterface = typeDecl==null ? false : isInterface(typeDecl);
    cl.skipInnerObfuscation = hasAnnotation(NoInnerObfuscation.class.getSimpleName(), modifiers);
//    if(typeDecl.typeParameters().size()>0)
//    System.out.println(typeDecl.typeParameters().get(0) + " :: " + typeDecl.typeParameters().get(0).getClass());
   
    classes.put(name, cl);
    classesList.add(cl);
   
    if(typeDecl != null)
    for(JCTypeParameter tp : (List<JCTypeParameter>)typeDecl.getTypeParameters()) {
      //System.out.println(name+" :: " + tp.typeBounds());
      VarType vt = new VarType(tp.getName());
      cl.parameters.add(vt);
      if(null!=tp.getBounds()) {
        if(tp.getBounds().size()>1) throwCompileError(typeDecl, "More than one bounded parameter type is not supported");
        if(tp.getBounds().size()==1) {
          vt.setBound(new VarType(tp.getBounds().get(0)));
          //System.out.println(vt);
        }
      }
    }
   
    cl.isNative = hasAnnotation(Native.class.getSimpleName(), modifiers);
    if(null != superclassType && !hasAnnotation(IgnoreExtends.class.getSimpleName(), modifiers)) {
           
      VarType superType = new VarType(superclassType);
      String superTypeName = superType.getName();
     
      if(hasAnnotation(ExtendsNative.class.getSimpleName(), modifiers)) {
        superTypeName = getAnnotationValue(ExtendsNative.class.getSimpleName(), modifiers);
      }
     
      if(!classes.containsKey(superTypeName)) throwCompileError(typeDecl, "Unknown superclass: " + superTypeName);
      cl.superClass = classes.get(superTypeName);
      cl.staticFields.addAll(cl.superClass.staticFields);
      cl.staticMethods.addAll(cl.superClass.staticMethods);
     
      if(VarType.isParameterizedType(superclassType)) {
        for(ParsedMethod m : cl.superClass.methods.values()) {
          cl.methods.put(m.name, new ParsedMethod(m, m.retType.implement(cl.superClass.parameters, superType)));
        }
        for(ParsedField f : cl.superClass.fields.values()) {
          cl.fields.put(f.name, new ParsedField(f, f.type.implement(cl.superClass.parameters, superType)));
        }
        //System.out.println(superclassType);
      } else
        cl.fields.putAll(cl.superClass.fields);
        cl.methods.putAll(cl.superClass.methods);
      }
    }

   
    Set<String> myProperties = new TreeSet<String>();
    for(Object decl : bodies) {
      if(decl instanceof JCVariableDecl) {
        JCVariableDecl field = (JCVariableDecl)decl;
        boolean isStatic = isStatic(field.getModifiers());
       
        String fName = field.getName().toString();
        if(myProperties.contains(fName) || cl.fields.containsKey(fName)) {
          throw new RuntimeException("Dublicate field or method: " + name+"."+fName);
        }
        myProperties.add(fName);
        if(isStatic) cl.staticFields.add(fName);
        cl.fields.put(fName, new ParsedField(fName, field.getType(), cl));
        //System.out.println(name+" :: "+fr.getName().toString()+ " :: " + field.getType());
      }
      if(decl instanceof JCMethodDecl) {
        JCMethodDecl method = (JCMethodDecl)decl;
        String mName = method.getName().toString();
       
        if(isConstructor(method)) {
          if(method.getBody().getStatements().size() == 0) continue;
          if(cl.constructor != null) {
            printWarning("Dublicate constructor in class " + name);
          } else cl.constructor = method;
        } else {
          if(!cl.isNative && myProperties.contains(mName)) {
            throwCompileError(method, "Dublicate field or method: " + name+"."+mName);
          }
         
          if(cl.methods.containsKey(mName) && cl.methods.get(mName).decl.getParameters().size()!=method.getParameters().size()) {
            throwCompileError(method, "Dublicate method: " + name+"."+mName);           
          }
         
          myProperties.add(mName);
          if(isStatic(method.getModifiers())) cl.staticMethods.add(mName);
          cl.methods.put(mName, new ParsedMethod(mName, method, cl, null));
          //System.out.println(name+" :: "+mName+ " :: " + method.getReturnType2());
        }
      }
    }
   
//    System.out.println(name);
//    for(ParsedField f : cl.fields.values()) {
//      System.out.println(name+"."+f.name+" :: " + f.type);
//    }
   
    return cl;
  }



  private void addLabelIfNeeded() {
    if(null != currentLabel) {
      code.append(currentLabel+": ");
      currentLabel = null;
    }
  }
 
  private VarType getFieldType(VarType type, String n) {
    if(type.isArray() && n.equals("length")) return VarType.NUMBER;
    ParsedClass cl;
    try {
      cl = getParsedClassByName(type.getName());
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
    if(null == cl) throw new RuntimeException("Unknown class: " + type + " :: "+n);
    ParsedField f = cl.fields.get(n);
    if(null == f) {
      throw new RuntimeException("Unknown field: " + type.getName()+"."+n);
    }
    return f.type.implement(cl.parameters, currentType);
  }

  private boolean isLocal(String n) {   
    return localVars.contains(n);
 

  public void parseMethodMainBlock(List<JCVariableDecl> params, JCBlock block) {
    int numLocal = localVars.size();
    //code.append("\n!!!     local: " + localVars);
    parseParameters("(", params, ")");
    String ip = indentPrefix;
    if(debugLevel > 0) indentPrefix += "\t";
    code.append(ifObfuscated("{", " {"+indentPrefix));
    if(anonymousTester.test(block)) {
      code.append("var "+(currentClass.lastElement().selfPrefix = getObfuscatedName("self" + currentClass.size()))+ifObfuscated("=this;", " = this;\n"+indentPrefix));
    } else currentClass.lastElement().selfPrefix = "this";
   
    if(params.size()>0) {
      JCVariableDecl lastParam = params.get(params.size()-1);
      String lpn = lastParam.getName().toString();
     
      if(lastParam.toString().indexOf("...") > 0) {
//        System.out.println("!!!" + lastParam);
//        code.append("var "+lpn+"=" + "[];"+indentPrefix);
        code.append("{var $$$=[];for(var i=0;i<arguments.length;i++)if(i>="+(params.size()-1)+")$$$.push(arguments[i]);"+getObfuscatedName(lpn)+"=$$$;}"+indentPrefix);         
//        System.out.println("###" + lastParam.);
      }
    }
   
    //if(null!=block)
    parseList(block.getStatements(), "");
    if(debugLevel>0) code.setLength(code.length()-1);
    code.append("}\n" + (indentPrefix = ip));
    //code.append("\n!!! END local: " + localVars);
    localVars.setSize(numLocal);
    currentType = null;
  }
 
 
  VarType throwCurrentClassesParameters(VarType t) {
    for(int i=currentClass.size()-1; i>=0; i--) {
      t = t.ifItIsParameter(currentClass.get(i).parameters);
    }
    return t;
  }
 
  private void throwCompileError(Tree node, String message) {
//    char[] source = lastSource;
//    int startPosition = node.getStartPosition();
//    int lineNumber = 1;
//    int posNumber = 0;
//    for(int i=0; i<=startPosition; i++) {
//      posNumber ++;
//      if(source[i] == '\t') posNumber += 3;
//      if(source[i] == '\n') {
//        lineNumber++;
//        posNumber = 0;
//      }
//    }
   
//    throw new RuntimeException("\n"+lastFileName+":"+lineNumber+":"+posNumber + "\n" +message);
    throw new RuntimeException("\n\n" + code.substring(Math.max(0, code.length()-100)) + "\n\n"
        +lastFileName+":\n" + message+"\n\n" + node+"\n\n");
  }
 
  public boolean compileClassFieldsAndMethods(ParsedClass cl) {
//    System.out.println(cl.name + " :: "+localVars);
   
   
    code.append("function " + getObfuscatedName(cl.name));
    if(cl.constructor == null/* && cl.superClass == null*/) {
      code.append("() {this."+getObfuscatedName(cl.name+"_initInstanceFields") + "();}\n"
        + getObfuscatedName(cl.name) + ".prototype." + getObfuscatedName(cl.name + "_constructor") + " = function(){}\n");
    } else {
      //parseMethodMainBlock(cl.constructor.parameters(), cl.constructor.getBody());
      int numLocals = localVars.size();
      parseParameters("(", cl.constructor.getParameters(), ")");
      code.append("{this."+getObfuscatedName(cl.name+"_initInstanceFields") + "();this." + getObfuscatedName(cl.name + "_constructor")+".apply(this, arguments)");
      code.append(";}\n");
      localVars.setSize(numLocals);
    }
   
    if(ignoredClasses.contains(cl.name)) return true;   
    code.append(ifObfuscated("", "\n"));
   
    String superType = null;
    if(null!=cl.type.getExtendsClause()) {
      superType = new VarType(cl.type.getExtendsClause()).getName();
      //System.out.println(superType+"="+classes.get(superType).isNative);
      if(!classes.get(superType).isNative) {
        code.append(getObfuscatedName(StrictWeb.class.getSimpleName())+"."+getObfuscatedName("extend")+"("+getObfuscatedName(cl.name)+", "+getObfuscatedName(superType)+");\n");
        code.append(getObfuscatedName(StrictWeb.class.getSimpleName())+"."+getObfuscatedName("extend")+"("+getObfuscatedName(cl.name)+".prototype, "+getObfuscatedName(superType)+".prototype);\n");
      }
     
      try {
        superType = getAnnotationValue(ExtendsNative.class.getSimpleName(), cl.type.getModifiers());
        code.append(getObfuscatedName(StrictWeb.class.getSimpleName())+"."+getObfuscatedName("extend")+"("+getObfuscatedName(cl.name)+", "+superType+");\n");
        code.append(getObfuscatedName(StrictWeb.class.getSimpleName())+"."+getObfuscatedName("extend")+"("+getObfuscatedName(cl.name)+".prototype, "+superType+".prototype);\n");       
      } catch(RuntimeException e) {
      }     
    }
   
    boolean hasAnonymousClassDeclaration = false;

    // only static fields
    for(JCTree tr : cl.type.getMembers()) if(tr instanceof JCVariableDecl){
      JCVariableDecl f = (JCVariableDecl)tr;
      hasAnonymousClassDeclaration |= anonymousTester.test(f);
     
      if(!isStatic(f.getModifiers())) continue;
      if(null==f.getInitializer() || "null".equals(f.getInitializer().toString())) continue;
     
      cl.selfPrefix = getObfuscatedName(cl.name)+ ".";
     
//      parse(f.getName());
     
      String fName = f.getName().toString();
//      if("name".equals(fName)) fName = "__name";
      code.append(cl.selfPrefix + getObfuscatedName(fName) + (obfuscate ? "=" : " = "));
      parse(f.getInitializer());
      code.append(";\n");
    }

    code.append(getObfuscatedName(cl.name) + ".prototype."+getObfuscatedName(cl.name+"_initInstanceFields") + " = function()" + ifObfuscated("{", " {\n"));
   
    if(null!=superType && classes.containsKey(superType) && !classes.get(superType).isNative) {
      code.append("this."+getObfuscatedName(superType+"_initInstanceFields") + "();" + ifObfuscated("", "\n"));
    }
   
//     only NOT static fields

    if(hasAnonymousClassDeclaration) {
      code.append("var "+(cl.selfPrefix = getObfuscatedName("self"))+" = this;\n");
    } else cl.selfPrefix = "this";
   
    for(JCTree tr : cl.type.getMembers()) if(tr instanceof JCVariableDecl){
      JCVariableDecl f = (JCVariableDecl)tr;
     
      if(isStatic(f.getModifiers())) continue;
      if(null==f.getInitializer() || "null".equals(f.getInitializer().toString())) continue;

      //cl.selfPrefix = "this";
     
      parse(f.getName());
      code.append(obfuscate ? "=" : " = ");
      parse(f.getInitializer());
      code.append(";\n");
    }
   
    code.append("}\n");
   
    // Methods
    if(!isInterface(cl.type))
    for(JCTree tr : cl.type.getMembers()) if(tr instanceof JCMethodDecl){
      JCMethodDecl m = (JCMethodDecl)tr;
     
      //if(m.isConstructor()) continue;
      //System.out.println(m.modifiers());
      String mName = m.getName().toString();
     
      if(isConstructor(m)) {
        if(cl.constructor == null) continue;
        mName = cl.name;
      }
     
      if(isAbstract(m.getModifiers())) continue;
      if(hasAnnotation(Native.class.getSimpleName(), m.getModifiers())) continue;

      boolean isStatic = isStatic(m.getModifiers());
      boolean isFinal  = isFinal(m.getModifiers());
      boolean isNative = hasAnnotation(NativeCode.class.getSimpleName(), m.getModifiers());
     
      currentParsedMethod = cl.methods.get(mName);
            if(currentParsedMethod != null && currentParsedMethod.mayBeExcluded) {
          code.append("/*method " + currentParsedMethod.id + " start*/\n");
      }
     
      if(!isStatic && !isFinal && !isConstructor(m)) {
        code.append(getObfuscatedName(cl.name) + ".prototype." + getObfuscatedName(cl.name + "_" + mName) + ifObfuscated("=", " =\n"));// + methodName +";\n");
      }
     
      String methodName = getObfuscatedName(cl.name) + (isStatic ? "." : ".prototype.") + getObfuscatedName(isConstructor(m) ? mName + "_constructor" : mName, cl.skipInnerObfuscation);
      code.append(methodName + ifObfuscated("=function", " = function"));
      if(isNative) {
        boolean wasObfuscated = obfuscate;
        obfuscate = false;
       
        int numLocal = localVars.size();
        parseParameters("(", m.getParameters(), ")");
        obfuscate = wasObfuscated;
       
        String nativeCode = getAnnotationValue(NativeCode.class.getSimpleName(), m.getModifiers());
        Matcher mat = toObfuscatePat.matcher(nativeCode);
        while(mat.find()) {
          try {
            nativeCode = nativeCode.replace(mat.group(), getObfuscatedName(mat.group(1)));
          }catch(Throwable e) {}
        }
//        System.out.println("nativeCode: " + nativeCode);
        code.append(nativeCode+"\n");
        localVars.setSize(numLocal);
      } else {
        //if(null == m.getBody()) System.out.println(methodName);
        //selfPrefix = "this";
        //System.out.println(m);
        parseMethodMainBlock(m.getParameters(), m.getBody());
      }

            if(currentParsedMethod != null && currentParsedMethod.mayBeExcluded) {
                code.append("/*method " + currentParsedMethod.id + " stop*/\n");
            }
            currentParsedMethod = null;
      //code.append("\n");
    }
    code.append(ifObfuscated("", "\n"))
    return true;
  }

  Pattern toObfuscatePat = Pattern.compile("\\%([\\$a-zA-Z_][\\$a-zA-Z_0-9]*)\\%");
 
  public void setParser(Parser parser) {
  }
 
  public boolean compileClassInitializers(ParsedClass cl) {
    if(cl.type == null) return false;
    for(Object o : cl.type.getMembers()) {
      if(o instanceof JCBlock
        && (((JCBlock)o).flags & Flags.STATIC) != 0) parse(o);
    }
    return true;
  }
 
  public void parseJCTree_JCClassDecl(com.sun.tools.javac.tree.JCTree.JCClassDecl type) {
    String name = type.getSimpleName().toString();
 
    ParsedClass cl = createNewParsedClass(name, type.getExtendsClause(), type.getModifiers(), type.getMembers(), type);
//    cl.sourceCode = lastSource;
    cl.fileName = lastFileName;
    //System.out.println(cl.name);
    for(Object o : type.getMembers()) {
      if(o instanceof JCClassDecl)
      throw new RuntimeException("Inner classes are not supported: " + name+"."+o);
    }
  }
 
  public void parseJCTree_JCCompilationUnit(com.sun.tools.javac.tree.JCTree.JCCompilationUnit node) {

//    System.out.println(node.getSourceFile());
    lastFileName = node.getSourceFile().toString();

   
    for(JCImport im : node.getImports()) {
      currentImports.add(im.getQualifiedIdentifier().toString());
    }

    parseList(node.getTypeDecls(), "\n");
  }

  public void parseJCTree_JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl node) {
    String n = node.getName().toString();
    localVars.add(n);
    localVarsTypes.put(n, throwCurrentClassesParameters(new VarType(node.getType())));
    if(parsingParameters) {
      code.append(getObfuscatedName(n));
    } else {
      code.append("var " + getObfuscatedName(n));
      code.append(obfuscate ? "=" : " = ");
      parse(node.getInitializer());
      code.append(";"+indentPrefix);
      currentType = null;
    }
  }
 
  public void parseJCTree_JCReturn(com.sun.tools.javac.tree.JCTree.JCReturn node) {
    code.append("return ");
    parse(node.getExpression());
    code.append(";" + indentPrefix);
    currentType = null;
  }
 
  public void parseJCTree_JCSkip(com.sun.tools.javac.tree.JCTree.JCSkip node) {
      code.append(";");
  }
 
  public void parseJCTree_JCNewClass(com.sun.tools.javac.tree.JCTree.JCNewClass node) {
    VarType type = new VarType(node.clazz);
   
    if(null != node.getClassBody()) {
//      anonymousClassDeclarationPresent = true;
//      if(anonymousClassDeclarationTest) return;
      JCClassDecl acl = node.getClassBody();
//      System.out.println(type.getName());
      ParsedClass claz = classes.get(type.getName());
      int i = 0;
      String ip = indentPrefix;
      if(debugLevel > 0) indentPrefix += "\t";
      if(null == claz || !claz.isInterface) {
        code.append(getObfuscatedName(StrictWeb.class.getSimpleName())+"."+getObfuscatedName("extend")+"(new " + getObfuscatedName(claz));
        parseArguments("(", node.getArguments(), ")");
        code.append(", {");
      } else code.append("{");
     
      code.append(indentPrefix);
     
      ParsedClass cl = createNewParsedClass(getTempIndex()+type.getName(), node.clazz, null, acl.getMembers(), null);
      cl.isNative = claz.isNative;
      cl.skipInnerObfuscation = claz.skipInnerObfuscation;
     
      currentClass.add(cl);
      cl.skipInnerObfuscation = claz.skipInnerObfuscation;
      for(Object o : acl.getMembers()) {
        if(o instanceof JCVariableDecl) {
          JCVariableDecl f = (JCVariableDecl)o;
          if(i>0) code.append(","+indentPrefix);
          i++;
          code.append(getObfuscatedName(f.getName().toString(), cl.skipInnerObfuscation || cl.isNative) + ": ");
          parse(f.getInitializer());
        }
        if(o instanceof JCMethodDecl) {
          if(i>0) code.append(","+indentPrefix);
          i++;
          JCMethodDecl m = (JCMethodDecl)o;
//          System.out.println(m.getName().toString() + " :: " + cl.skipInnerObfuscation + " :: " + cl.isNative);
          code.append(getObfuscatedName(m.getName().toString(), cl.skipInnerObfuscation || cl.isNative) + ": function");
          parseMethodMainBlock(m.getParameters(), m.getBody());
        }
      }
      if(debugLevel>0) code.setLength(code.length()-1);
      code.append(null == claz || !claz.isInterface ? "})" : "}");
      indentPrefix = ip;
      currentType = type;
      currentClass.setSize(currentClass.size()-1);
      return;
    }
   
  //    if(null != node.getExpression()) {
//      parse(node.getExpression());
//      code.append('.');
//    }
   
    String spMethodName = "specialInstanceCreation" + type.getName();
    try {
      Method spInvMethod = this.getClass().getMethod(spMethodName, JCNewClass.class);
      spInvMethod.invoke(this, node);
    }catch(Exception e) {
      try {
        ParsedClass cl = classes.get(type.getName());
        if(cl == null) throw new RuntimeException("Cant find class to create: " + type.getName());
        if(!cl.canCreateNewInstance) throw new RuntimeException("Cant creat new instance of: " + type.getName());
        code.append("new "+getObfuscatedName(cl));
        parseArguments("(", node.getArguments(), ")");
        currentType = type;
      }catch(RuntimeException re) {
        throw new RuntimeException(re.getMessage() + "\n\nTry to add method:\n\npublic void " + spMethodName +"(JCNewClass node) {\n\tthrow new RuntimeException(\"FIX ME\");\n}\n");
      }
    }   

  }

  void parseSimpleName(Tree node, String n) {

    if("this".equals(n)) {
      code.append(currentClass.lastElement().selfPrefix);
      currentType = currentClass.lastElement().varType;
      return;
    }
   
    if("super".equals(n)) {
      superInvocation = currentClass.lastElement().superClass.name+"_";
      code.append(currentClass.lastElement().selfPrefix);
      currentType = currentClass.lastElement().varType;
      return;
    }
   
//    System.out.println(node);
   
//        if(n.equals("FileUploaderOptions")) {
//          System.out.println("\nisLocal: " + n + " in " + localVars+"\n"+currentClass.lastElement().varType);
//        }
       
    if(!isLocal(n)) {     
      currentType = null;//currentClass.lastElement().varType;
      ParsedClass cl = null;
      for(int i=currentClass.size()-1; i>=0; i--) {
        cl = currentClass.get(i);
        ParsedField field = cl.fields.get(n);
        if(null != field) {
          if(cl.staticFields.contains(n)) {
            ParsedField f = cl.fields.get(n);
            code.append(getObfuscatedName(f.cl)+".");
          } else {
            if(null==cl.selfPrefix/* && !anonymousClassDeclarationTest*/) {
              throwCompileError(node, "null==cl.selfPrefix");
            }
            code.append(cl.selfPrefix + ".");
          }
         
          currentType = field.type;
          break;
        }       
      }
      if(null == currentType) {
        if(!classes.containsKey(n)) throwCompileError(node, "Unknown static something: " + n);
        currentType = (cl=classes.get(n)).varType;
                code.append(getObfuscatedName(n, cl.isNative));
      } else {     
          //      System.out.println(cl.name +"::"+n + "  ---  " + (cl.isNative || cl.skipInnerObfuscation));
          code.append(getObfuscatedName(n, cl.isNative || cl.skipInnerObfuscation));
      }
    } else {
      currentType = localVarsTypes.get(n);
      code.append(getObfuscatedName(n));   
    }
  }
 
  public String getObfuscatedName(ParsedClass cl) {
    return cl.isNative ? cl.name : getObfuscatedName(cl.name);
  }

  Map<String, String> obfuscatedNames = new HashMap<String, String>();
 
  private String getObfuscatedName(String n, boolean noObf) {
    return noObf ? n : getObfuscatedName(n);
  }
 
  public String ifObfuscated(String obfusc, String normal) {
    return obfuscate ? obfusc : normal;
  }
 
  public String getObfuscatedName(String n) {
    if(!obfuscate || n.length()<3) return n;
    return getObfuscatedForce(n);
  }

    private String getObfuscatedForce(String n) {
        String on = obfuscatedNames.get(n);
    if(on == null) {
//      obfuscatedNames.put(n, on = "/*"+n+"*/$"+Integer.toString(obfuscatedNames.size(), 36));
      obfuscatedNames.put(n, on = "$"+Integer.toString(obfuscatedNames.size(), 36));
    }
    return on;
    }

  public void parseJCTree_JCIdent(com.sun.tools.javac.tree.JCTree.JCIdent node) {
    parseSimpleName(node, node.toString());   
  }
 
  private void parseSuperConstructorInvocation(com.sun.tools.javac.tree.JCTree.JCMethodInvocation inv) {
    ParsedClass superclassType = currentClass.lastElement().superClass;
    if(null == superclassType) return;
    code.append("this." + getObfuscatedName(superclassType.name + "_constructor"));
    int nl = localVars.size();
    parseArguments("(", inv.getArguments(), ")");
    localVars.setSize(nl);
    code.append(";"+indentPrefix);
    currentType = null;
  }

 
  public void parseJCTree_JCMethodInvocation(com.sun.tools.javac.tree.JCTree.JCMethodInvocation inv) {
//    System.out.println("INV: " + inv);
   
    JCFieldAccess fa = null;
    String mName = null;
    if(inv.meth instanceof JCFieldAccess) {
      fa = (JCFieldAccess)inv.meth;
      mName = fa.getIdentifier().toString();
    } else {
      mName = inv.meth.toString();
    }
   
    if("super".equals(mName)) {
      parseSuperConstructorInvocation(inv);
      return;
    }
   
   
    if(null!=fa && null != fa.getExpression()) {
//      System.out.println("PARSE EXPT:: " + fa.getExpression().getClass());
      parse(fa.getExpression());
    } else {
      for(int i=currentClass.size()-1; i>=0;i--) {
        ParsedClass cl = currentClass.get(i);
        if(cl.staticMethods.contains(mName)) {
          ParsedMethod m = cl.methods.get(mName);
          code.append(getObfuscatedName(m.cl));
          currentType = cl.varType;
          break;
        } else if(cl.methods.containsKey(mName)) {
          code.append(cl.selfPrefix);
          currentType = cl.varType;
          break;
        }
      }
    }
 
    if(null == currentType) throwCompileError(inv, "Unknown type of method invocation: "+mName+"\n" + inv);
    if(debugLevel>1) code.append("/*"+currentType.getName()+"*/");
   
    if(ignoredClasses.contains(currentType.getName())) {
      // Игнорируем вызовы методов данного класса
      //code.append(";");
      return;
    }
   
    code.append(".");
   
    try {
      String spInvMethodName = "specialInvocationMethod" + currentType.getName()+"_"+mName;
      Method spInvMethod = this.getClass().getMethod(spInvMethodName, JCMethodInvocation.class);
      spInvMethod.invoke(this, inv);
    }catch(Exception e) {
        for(int i=plugins.size()-1; i>=0; i--) {
        if(plugins.get(i).invokeMethod(mName, inv, inv.getArguments())) return;
      }
    }   
  }

  public void parseJCTree_JCIf(com.sun.tools.javac.tree.JCTree.JCIf node) {
    code.append("if");
    parse(node.getCondition());
//    code.append(")");
    parse(node.getThenStatement());
    if(null != node.getElseStatement()) {
      code.append(" else ");
      parse(node.getElseStatement());
    }   
  }
 
  public void parseJCTree_JCBinary(com.sun.tools.javac.tree.JCTree.JCBinary node) {
    parse(node.getLeftOperand());
    code.append(operatorName(node.tag));
    parse(node.getRightOperand());
  }
 

  public void parseJCTree_JCLiteral(com.sun.tools.javac.tree.JCTree.JCLiteral node) {
   
    switch(node.typetag) {
    case TypeTags.BYTE:
    case TypeTags.SHORT:
    case TypeTags.INT:
    case TypeTags.LONG:
    case TypeTags.FLOAT:
    case TypeTags.DOUBLE:
      code.append(node.value);
      if(currentType != VarType.STRING) currentType = VarType.NUMBER;
      break;
    case TypeTags.BOT:
      code.append("null");
      if(currentType != VarType.STRING) currentType = null;
      break;
    case TypeTags.BOOLEAN:
      code.append((Integer)node.value==1? "true":"false");
      if(currentType != VarType.STRING) currentType = VarType.BOOLEAN;
      break;
    case TypeTags.CHAR:     
      code.append(safe(""+(char)((int)(Integer)node.value)));
      currentType = VarType.STRING;
      break;
    default:
      code.append(safe(node.value.toString()));
      currentType = VarType.STRING;       
    }   
  }
 
  public void parseJCTree_JCExpressionStatement(com.sun.tools.javac.tree.JCTree.JCExpressionStatement st) {
    currentType = null;
    parse(st.getExpression());
    if(!inlineStatement) code.append(ifObfuscated(";", ";"+indentPrefix));
    currentType = null;
  }
 
  public void parseJCTree_JCAssign(com.sun.tools.javac.tree.JCTree.JCAssign node) {
    currentType = null;
    parse(node.getVariable());
    code.append("=");
    parse(node.getExpression());
  }
 
    public void parseJCTree_JCAssignOp(com.sun.tools.javac.tree.JCTree.JCAssignOp node) {
    currentType = null;
    parse(node.getVariable());
    code.append(operatorName(node.tag-17)+"=");
    parse(node.getExpression());
    }
   
  public void parseJCTree_JCEnhancedForLoop(com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop node) {
    StringBuilder temp = code;
    code = new StringBuilder();
    parse(node.getExpression());
    code = temp;
   
    if(currentType.nameIs("Map") || currentType.nameIs("Set")
        || currentType.nameIs("TreeMap") || currentType.nameIs("TreeSet")
            || currentType.nameIs("HashMap") || currentType.nameIs("HashSet")
        ) {     
      currentType = null;
      addLabelIfNeeded();
      code.append("for(var ");
      parsingParameters = true;
      parse(node.getVariable());
      parsingParameters = false;
      code.append(" in ");
      parse(node.getExpression());
      code.append(")");
      parse(node.getStatement());
      currentType = null;
    } else { //if(currentType.nameIs("List") || currentType.nameIs("Collection") || currentType.isArray()) {
      currentType = null;
      String indexName = getObfuscatedForce("_i" + getTempIndex());
      String listName  = getObfuscatedForce("_list" + getTempIndex());
      code.append("{var "+listName+"=");
      parse(node.getExpression());     
      code.append(";");
      addLabelIfNeeded();
      code.append("for(var "+indexName+"=0;"+indexName+"<"+listName+".length;"+indexName+"++) {var ");
      parsingParameters = true;
      parse(node.getVariable());
      parsingParameters = false;
      code.append("="+listName+"["+indexName+"];");
      parse(node.getStatement());
      code.append("}}"+indentPrefix);
      currentType = null;     
    } //else throw new RuntimeException("Unsupported type for enhancedForStatement: " + currentType.getName());
  }

  public void parseName(com.sun.tools.javac.util.Name node) {
    parseSimpleName(null, node.toString());   
  }
 
  public void parseJCTree_JCTypeCast(com.sun.tools.javac.tree.JCTree.JCTypeCast node) {
    parse(node.getExpression());
  }
 
  public void parseJCTree_JCDoWhileLoop(com.sun.tools.javac.tree.JCTree.JCDoWhileLoop node) {
    addLabelIfNeeded();
    code.append("do");
    parse(node.getStatement());
    code.append("while");
    parse(node.getCondition());
    code.append(";");
  }
 
  public void parseJCTree_JCWhileLoop(com.sun.tools.javac.tree.JCTree.JCWhileLoop node) {
    addLabelIfNeeded();
    code.append("while");
    parse(node.getCondition());
    parse(node.getStatement());
  }

  public void parseJCTree_JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess node) {
    parse(node.getExpression());
    String n = node.getIdentifier().toString();
    if(null == currentType) throw new RuntimeException("Unknown type in declaration: " + node);
    if(debugLevel>1) code.append("/*"+currentType.getName()+"*/");
   
    ParsedClass cl = null;
        try {
            cl = getParsedClassByName(currentType.getName());
        } catch (NoSuchMethodException e) {
        }
//    getClassSafe(currentType.getName())
   
//    if("name".equals(n) && cl!=null && cl.staticFields.contains(n)) {
//      code.append(".__name");
//    } else {
//      code.append("." + n);
//    }
       
//    System.out.println(currentType.getName() +"::"+n + "  ===>  " + (cl!=null && (cl.isNative || cl.skipInnerObfuscation)));
       
    code.append("." + getObfuscatedName(n,  currentType.isArray() || (cl!=null && (cl.isNative || cl.skipInnerObfuscation))));
    currentType = getFieldType(currentType, n);       
  }
 
  public void parseJCTree_JCBlock(com.sun.tools.javac.tree.JCTree.JCBlock block) {
    currentType = null;
    int numLocal = localVars.size();
    String ip = indentPrefix;
    if(debugLevel > 0) indentPrefix += "\t";
    code.append(ifObfuscated("{", " {"+indentPrefix));
    parseList(block.getStatements(), "");
    if(debugLevel>0) code.setLength(code.length()-1);
    code.append(ifObfuscated("}", "}" + (indentPrefix = ip)));
    localVars.setSize(numLocal);
    currentType = null;
  }
 
  public void parseJCTree_JCConditional(com.sun.tools.javac.tree.JCTree.JCConditional node) {
    parse(node.getCondition());
    code.append("?");
    parse(node.getTrueExpression());
    code.append(":");
    parse(node.getFalseExpression());
  }
 
  public void parseJCTree_JCNewArray(com.sun.tools.javac.tree.JCTree.JCNewArray node) {
    currentType = null;
    if(node.getDimensions().size() > 1) throw new RuntimeException("ArrayCreation with dimention > 1:\n" + node);

    code.append('[');
    if(null != node.getInitializers()) parseList(node.getInitializers(), ",");
    code.append(']');   
   
    currentType = new VarType(node.getType());
  }
 
  public void parseJCTree_JCArrayAccess(com.sun.tools.javac.tree.JCTree.JCArrayAccess node) {
    parse(node.getExpression());
    //System.out.println(currentType);
    VarType t = currentType.parametersGet(0);
    code.append("[");
    parse(node.getIndex());
    code.append("]");
    //System.out.println(currentType);
    currentType = t;
  }
 
  public void parseJCTree_JCParens(com.sun.tools.javac.tree.JCTree.JCParens node) {
    code.append('(');
    parse(node.getExpression());
    code.append(')')
  }
 
  public void parseJCTree_JCForLoop(com.sun.tools.javac.tree.JCTree.JCForLoop node) {
    addLabelIfNeeded();
    code.append("for(");
//    System.out.println(node.getInitializer());
    StringBuilder c = code;
    code = new StringBuilder();
    parseList(node.getInitializer(), ", ");
    c.append(code.toString().replaceAll(";\\s+, var", ", "));
    code = c;
    if(node.getInitializer().size() == 0)code.append(";");
    parse(node.getCondition());
    code.append(";");
    inlineStatement = true;
    parseList(node.getUpdate(), ", ");
    inlineStatement = false;
    code.append(")");
    parse(node.getStatement());
  }
 
  public void parseJCTree_JCUnary(com.sun.tools.javac.tree.JCTree.JCUnary node) {
    if(node.tag <= 51) {
      code.append(operatorName(node.tag));
      parse(node.getExpression());     
    } else {
      parse(node.getExpression());
      code.append(operatorName(node.tag));
    }
  }
 
  public void parseJCTree_JCBreak(com.sun.tools.javac.tree.JCTree.JCBreak node) {
    code.append("break");
    if(null!=node.getLabel()) code.append(" " + node.getLabel());
    code.append(";"+indentPrefix);
  }
 
  public void parseJCTree_JCTry(com.sun.tools.javac.tree.JCTree.JCTry node) {
    code.append("try");
    parse(node.getBlock());
    if(node.getCatches().size() > 0) {
      if(node.getCatches().size() != 1) throwCompileError(node, "many catch does not support yet");
      JCCatch cc = node.getCatches().get(0);
      code.append("catch(");
      parsingParameters = true;
      parse(cc.getParameter());
      parsingParameters = false;
      code.append(")");
      parse(cc.getBlock());
    }
   
    if(node.getFinallyBlock() != null) {
      code.append("finally");
      parse(node.getFinallyBlock());
    }
  }
 
  public void parseJCTree_JCContinue(com.sun.tools.javac.tree.JCTree.JCContinue node) {
    code.append("continue");
    if(null!=node.getLabel()) code.append(" " + node.getLabel());
    code.append(";"+indentPrefix);
  }
 
  public void parseJCTree_JCThrow(com.sun.tools.javac.tree.JCTree.JCThrow node) {
    code.append("throw ");
    parse(node.getExpression());
    code.append(";" + indentPrefix);
    currentType = null;
  }
 
  public void parseJCTree_JCLabeledStatement(com.sun.tools.javac.tree.JCTree.JCLabeledStatement node) {
    currentLabel = node.getLabel().toString();
    parse(node.getStatement());
  }

  public void addClassLabel(Class clazz, String label) {
    Set<String> labels = classLabels.get(clazz.getSimpleName());
    if(labels == null) {
      classLabels.put(clazz.getSimpleName(), labels = new HashSet<String>());
    }
    labels.add(label);
  }
 
  public boolean hasClassLabel(String className, String label) {
    return classLabels.containsKey(className) && classLabels.get(className).contains(label);
  }

  public void addIgnoredClasses(Collection<String> ignoredClasses) {
    this.ignoredClasses.addAll(ignoredClasses);
  }

    public String getAllCode() {
        String allCode = code.toString();
       
        for(ParsedClass cl: classesList) {
            methods: for(ParsedMethod m: cl.methods.values()) if(m.cl == cl && m.mayBeExcluded) {

                int count = m.invokeCounter.count;
                if(count > 0 && m.invokeCounter.invokers.size() > 0) {
                    for(ParsedMethod inv: m.invokeCounter.invokers.keySet())
                        if(!inv.mayBeExcluded || inv.invokeCounter.count > 0) continue methods;
                }
                Pattern p = Pattern.compile("\\/\\*method "+m.id+" start\\*\\/\n.*\\/\\*method "+m.id+" stop\\*\\/\n", Pattern.DOTALL);
                allCode = p.matcher(allCode).replaceAll("");
//                System.out.println("Exclude " + m.cl.name+"::"+m.name);
            }
        }
       
        allCode = allCode.replaceAll("\\/\\*method [0-9]+-[0-9]+ start\\*\\/\n", "");
        allCode = allCode.replaceAll("\\/\\*method [0-9]+-[0-9]+ stop\\*\\/\n", "");
       
        return allCode;
    }
}
TOP

Related Classes of ru.yandex.strictweb.scriptjava.compiler.Parser

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.