public static List <Java_Method> java_Methods(this Java_Class _class)
 {
     if (_class.notNull())
     {
         return(_class.Methods);
     }
     return(new List <Java_Method>());
 }
        public static string file(this Java_Class _class)
        {
            var file = (_class.Signature.EndsWith(_class.Name))
                                                        ? _class.Signature.subString(0, _class.Signature.size() - _class.Name.size())
                                                        : _class.Signature;

            file = "{0}{1}".format(file.replace(".", "\\"), _class.SourceFile);
            return(file);
        }
        public static Java_Class map_Annotations(this Java_Class javaClass, object classFile)
        {
            var annotations = (Object[])classFile.prop("Annotations");

            if (annotations.notNull())
            {
                javaClass.Annotations = annotations;
            }
            return(javaClass);
        }
        public static Java_Class map_Interfaces(this Java_Class javaClass, object classFile)
        {
            var interfaces = (IEnumerable)classFile.prop("Interfaces");

            foreach (var _interface in interfaces)
            {
                javaClass.Interfaces.Add(_interface.prop("Name").str());
            }
            return(javaClass);
        }
        public static TreeNode add_ConstantsPool(this TreeNode treeNode, Java_Class _class)
        {
            var constantsPoolNode = treeNode.add_Node("_ConstantsPool");

            foreach (var item in _class.constantsPool_byType())
            {
                constantsPoolNode.add_Node(item.Key, item.Value, true);
            }
            return(treeNode);
        }
        public static List <string> methodRefs_old(this Java_Class methodClass, Java_Method method)
        {
            var constants_byType = method.constantsPool_byType(methodClass);

            if (constants_byType.hasKey("Methodref"))
            {
                return(constants_byType["Methodref"].values());
            }
            return(new List <string>());
        }
 public static Java_Class map_EnclosingMethod(this Java_Class javaClass, object classFile)
 {
     if (classFile.prop("EnclosingMethod").notNull())
     {
         var enclosingMethodData = (string[])classFile.prop("EnclosingMethod");
         if (enclosingMethodData.size() == 3)                // when the size it is 1, it represents the case of normal inner classes (not annonymous classes defined inside methods which is what I'm after)
         {
             javaClass.EnclosingMethod = "{0}.{1}{2}".format(enclosingMethodData[0], enclosingMethodData[1], enclosingMethodData[2]);
         }
     }
     return(javaClass);
 }
        public static Java_Class map_Methods(this Java_Class javaClass, object classFile)
        {
            var mappedConstantsPools = javaClass.ConstantsPool.getDictionaryWithValues();
            var methods = (IEnumerable)classFile.prop("Methods");

            foreach (var method in methods)
            {
                var javaMethod = new Java_Method {
                    Name = method.prop("Name").str(),
                    ParametersAndReturnType = method.prop("Signature").str(),
                    ClassName          = javaClass.Signature,
                    IsAbstract         = method.prop("IsAbstract").str().toBool(),
                    IsClassInitializer = method.prop("IsClassInitializer").str().toBool(),
                    IsNative           = method.prop("IsNative").str().toBool(),
                    IsPublic           = method.prop("IsPublic").str().toBool(),
                    IsStatic           = method.prop("IsStatic").str().toBool()
                };
                javaMethod.SimpleSignature = "{0}{1}".format(javaMethod.Name, javaMethod.ParametersAndReturnType);
                javaMethod.ReturnType      = javaMethod.ParametersAndReturnType.subString_After(")");
                javaMethod.Signature       = "{0}.{1}".format(javaMethod.ClassName, javaMethod.SimpleSignature);
                if (method.prop("GenericSignature").notNull())
                {
                    javaMethod.GenericSignature = method.prop("GenericSignature").str();
                }
                javaMethod.map_Annotations(method)
                .map_Variables(method);


                var instructions = (IEnumerable)method.prop("Instructions");
                if (instructions.notNull())
                {
                    foreach (var instruction in instructions)
                    {
                        var javaInstruction = new Java_Instruction();
                        javaInstruction.Pc           = instruction.prop("PC").str().toInt();
                        javaInstruction.OpCode       = instruction.prop("NormalizedOpCode").str();
                        javaInstruction.Target_Index = instruction.prop("TargetIndex").str().toInt();
                        javaMethod.Instructions.Add(javaInstruction);
                    }
                }

                javaClass.Methods.Add(javaMethod);
            }
            return(javaClass);
        }
        public static API_IKVMC_Java_Metadata create_JavaMetadata(this API_IKVMC ikvmc, string fileToProcess)
        {
            var o2Timer      = new O2Timer("Created JavaData for {0}".format(fileToProcess)).start();
            var javaMetaData = new API_IKVMC_Java_Metadata();

            javaMetaData.FileProcessed = fileToProcess;
            var classes = ikvmc.getRawClassesData_FromFile_ClassesOrJar(fileToProcess);

            foreach (DictionaryEntry item in classes)
            {
                var name      = item.Key.str();
                var bytes     = (byte[])item.Value.field("data");
                var classFile = ikvmc.createClassFile(bytes, 1);                                        // value of 1 gets the local variables
                var javaClass = new Java_Class {
                    Signature   = name,
                    Name        = name.contains(".") ? name.split(".").last() : name,
                    SourceFile  = classFile.prop("SourceFileAttribute").str(),
                    IsAbstract  = classFile.prop("IsAbstract").str().toBool(),
                    IsInterface = classFile.prop("IsInterface").str().toBool(),
                    IsInternal  = classFile.prop("IsInternal").str().toBool(),
                    IsPublic    = classFile.prop("IsPublic").str().toBool(),
                    SuperClass  = classFile.prop("SuperClass").str()
                };

                if (classFile.prop("GenericSignature").notNull())
                {
                    javaClass.GenericSignature = classFile.prop("GenericSignature").str();
                }

                javaClass.ConstantsPool = classFile.getConstantPoolEntries();
                javaClass.map_Annotations(classFile)
                .map_Interfaces(classFile)
                .map_Fields(classFile)
                .map_Methods(classFile)
                .map_EnclosingMethod(classFile);

                javaClass.map_LineNumbers(ikvmc.createClassFile(bytes, 2));                // for this we need to call createClassFile with the value of 2 (to get the source code references)

                javaMetaData.Classes.Add(javaClass);
                //break; //
            }
            o2Timer.stop();
            return(javaMetaData);
        }
        //we need to do this because the IKVMC doesn't have a option to get both fields and source code references at the same time
        public static Java_Class map_LineNumbers(this Java_Class javaClass, object classFileWithLineNumbers)
        {
            var methodsWithLineNumbers = ((IEnumerable)classFileWithLineNumbers.prop("Methods"));
            var currentMethodId        = 0;

            foreach (var methodWithLineNumbers in methodsWithLineNumbers)
            {
                var javaMethod = javaClass.Methods[currentMethodId];
                var lineNumberTableAttributes = (IEnumerable)methodWithLineNumbers.prop("LineNumberTableAttribute");
                if (lineNumberTableAttributes.notNull())
                {
                    foreach (var lineNumberTableAttribute in lineNumberTableAttributes)
                    {
                        javaMethod.LineNumbers.Add(new LineNumber {
                            Line_Number = lineNumberTableAttribute.field("line_number").str().toInt(),
                            Start_Pc    = lineNumberTableAttribute.field("start_pc").str().toInt()
                        });
                    }

                    var mapppedLineNumbers = new Dictionary <int, int>();
                    foreach (var lineNumber in javaMethod.LineNumbers)
                    {
                        mapppedLineNumbers.Add(lineNumber.Start_Pc, lineNumber.Line_Number);
                    }
                    var lastLineNumber = 0;
                    foreach (var instruction in javaMethod.Instructions)
                    {
                        if (mapppedLineNumbers.hasKey(instruction.Pc))
                        {
                            instruction.Line_Number = mapppedLineNumbers[instruction.Pc];
                            lastLineNumber          = instruction.Line_Number;
                        }
                        else
                        {
                            instruction.Line_Number = lastLineNumber;
                        }
                    }
                }
                currentMethodId++;
            }
            return(javaClass);
        }
        public static Java_Class map_Fields(this Java_Class javaClass, object classFile)
        {
            //var interfaces = (IEnumerable)classFile.prop("Interfaces");
            //foreach(var _interface in interfaces)
            //	javaClass.Interfaces.Add(_interface.prop("Name").str());
            var fields = (IEnumerable)classFile.prop("Fields");

            foreach (var field in fields)
            {
                javaClass.Fields.Add(new Java_Field
                {
                    Name          = field.prop("Name").str(),
                    Signature     = field.prop("Signature").str(),
                    ConstantValue = field.prop("ConstantValue").notNull()
                                                                                                                                ? field.prop("ConstantValue").str()
                                                                                                                                : null
                });
            }

            return(javaClass);
        }
        public static List <MethodCall> methodRefs(this Java_Method method, Java_Class methodClass)
        {
            var file             = methodClass.file();
            var constants_byType = method.constantsPool_byType(methodClass);

            if (constants_byType.hasKey("Methodref") || constants_byType.hasKey("InterfaceMethodref"))
            {
                var methodRefs_AtLines = method.getConstantsPoolUsage_byIndex_WithLineNumbers();
                var methodRefs         = new List <MethodCall>();

                if (constants_byType.hasKey("Methodref"))
                {
                    methodRefs.AddRange(constants_byType["Methodref"].get_MethodsRef_FromContantsPool(file, false, methodRefs_AtLines));
                }

                if (constants_byType.hasKey("InterfaceMethodref"))
                {
                    methodRefs.AddRange(constants_byType["InterfaceMethodref"].get_MethodsRef_FromContantsPool(file, true, methodRefs_AtLines));
                }
                return(methodRefs);
            }
            return(new List <MethodCall>());
        }
		public static API_IKVMC_Java_Metadata create_JavaMetadata(this API_IKVMC ikvmc, string fileToProcess)
		{
			var o2Timer = new O2Timer("Created JavaData for {0}".format(fileToProcess)).start();
			var javaMetaData = new API_IKVMC_Java_Metadata();
			javaMetaData.FileProcessed = fileToProcess;
			var classes = ikvmc.getRawClassesData_FromFile_ClassesOrJar(fileToProcess);
			foreach(DictionaryEntry item in classes)
			{
				var name = item.Key.str();
				var bytes = (byte[])item.Value.field("data");
				var classFile = ikvmc.createClassFile(bytes,1);				// value of 1 gets the local variables
				var javaClass = new Java_Class {
													Signature = name,													
													Name = name.contains(".") ? name.split(".").last() : name,
													SourceFile = classFile.prop("SourceFileAttribute").str(),
							 						IsAbstract = classFile.prop("IsAbstract").str().toBool(),
													IsInterface = classFile.prop("IsInterface").str().toBool(),
													IsInternal = classFile.prop("IsInternal").str().toBool(),
													IsPublic = classFile.prop("IsPublic").str().toBool(),
													SuperClass = classFile.prop("SuperClass").str()
											   };
				
				if (classFile.prop("GenericSignature").notNull())
					javaClass.GenericSignature = classFile.prop("GenericSignature").str();
					
				javaClass.ConstantsPool = classFile.getConstantPoolEntries();
				javaClass.map_Annotations(classFile)
						 .map_Interfaces(classFile)
						 .map_Fields(classFile)
						 .map_Methods(classFile)
						 .map_EnclosingMethod(classFile);								
						 				
				javaClass.map_LineNumbers(ikvmc.createClassFile(bytes,2)); // for this we need to call createClassFile with the value of 2 (to get the source code references)
				
				javaMetaData.Classes.Add(javaClass);												
				//break; // 
			}
			o2Timer.stop();
			return javaMetaData;
		}
		public static Dictionary<string,List<ConstantPool>> constantsPool_byType(this Java_Method method, Java_Class _class)
		{
			return _class.ConstantsPool.getDictionary_byType(method);
		}
        public static TreeNode add_Instructions(this TreeNode treeNode, Java_Method method, Java_Class methodClass)
        {
            var values = methodClass.ConstantsPool.getDictionaryWithValues();

            foreach (var instruction in method.Instructions)
            {
                var nodeText = "[line:{0}] \t {1}".format(instruction.Line_Number, instruction.OpCode);
                if (instruction.Target_Index > 0 && values.hasKey(instruction.Target_Index))
                {
                    nodeText = "{0} {1}".format(nodeText, values[instruction.Target_Index]);
                }
                treeNode.add_Node(nodeText, instruction)
                .color(Color.DarkGreen);
            }
            return(treeNode);
        }
 public static Dictionary <string, List <ConstantPool> > constantsPool_byType(this Java_Method method, Java_Class _class)
 {
     return(_class.ConstantsPool.getDictionary_byType(method));
 }
 public static Dictionary <int, ConstantPool> constantsPool_byIndex(this Java_Class _class)
 {
     return(_class.ConstantsPool.getDictionary_byIndex());
 }
 public static Dictionary <int, string> constantsPool_Values(this Java_Class _class)
 {
     return(_class.ConstantsPool.getDictionaryWithValues());
 }
 public static ascx_SourceCodeViewer showInCodeViewer(this ascx_SourceCodeViewer codeViewer, Java_Class _class, Java_Method method)
 {
     codeViewer.editor().showInCodeEditor(_class, method);
     return(codeViewer);
 }
 public static MethodCall_Location methodCall_Location(this Java_Class _class, int line, int pc)
 {
     return(new MethodCall_Location {
         File = _class.file(), Line = line, Pc = pc
     });
 }
		public static TreeNode add_Instructions(this TreeNode treeNode, Java_Method method, Java_Class methodClass)
		{
			var values = methodClass.ConstantsPool.getDictionaryWithValues();
			foreach(var instruction in method.Instructions)
			{
				var nodeText = "[line:{0}] \t {1}".format(instruction.Line_Number,instruction.OpCode);
				if (instruction.Target_Index > 0 && values.hasKey(instruction.Target_Index))								
					nodeText = "{0} {1}".format(nodeText , values[instruction.Target_Index]);
				treeNode.add_Node(nodeText, instruction)
						.color(Color.DarkGreen);
			
			}
			return treeNode;
		}
		public static TreeNode add_ConstantsPool(this TreeNode treeNode, Java_Method method, Java_Class methodClass)
		{
			var constantsPoolNode = treeNode.add_Node("_ConstantsPool");
			foreach(var item in method.constantsPool_byType(methodClass))
				constantsPoolNode.add_Node(item.Key, item.Value, true);
			return treeNode;
		}				
 public static List <Java_Method> java_Methods_Getters_Public(this Java_Class _class)
 {
     return((from method in _class.Methods
             where method.IsPublic && method.Name.starts("get")
             select method).toList());
 }
		public static ascx_SourceCodeViewer showInCodeViewer(this ascx_SourceCodeViewer codeViewer ,Java_Class _class, Java_Method method)
		{
			codeViewer.editor().showInCodeEditor(_class, method);
			return codeViewer;
		}
		public static List<MethodCall> methodRefs(this Java_Method method,  Java_Class methodClass)
		{
			var file = methodClass.file();
			var constants_byType = method.constantsPool_byType(methodClass);			
			if (constants_byType.hasKey("Methodref") || constants_byType.hasKey("InterfaceMethodref"))
			{
				var methodRefs_AtLines = method.getConstantsPoolUsage_byIndex_WithLineNumbers();
				var methodRefs = new List<MethodCall>();
				
				if (constants_byType.hasKey("Methodref"))
					methodRefs.AddRange(constants_byType["Methodref"].get_MethodsRef_FromContantsPool(file, false,  methodRefs_AtLines));
					
				if (constants_byType.hasKey("InterfaceMethodref"))
					methodRefs.AddRange(constants_byType["InterfaceMethodref"].get_MethodsRef_FromContantsPool(file, true,  methodRefs_AtLines));
				return methodRefs;
			}
			return new List<MethodCall>();
		}
        public static SourceCodeEditor showInCodeEditor(this SourceCodeEditor codeEditor, Java_Class _class, Java_Method method)
        {
            //var _class = classes_bySignature[classSignature];
            var file = _class.file();

            codeEditor.open(file);
            var lineNumber = 0;

            if (method.isNull() || method.LineNumbers.isNull())
            {
                return(codeEditor);
            }
            foreach (var item in method.LineNumbers)
            {
                if (item.Line_Number > 1)
                {
                    if (lineNumber == 0 || item.Line_Number < lineNumber)
                    {
                        lineNumber = item.Line_Number;
                    }
                }
            }

            //this to match the method name to the location (vs the first method)
            var sourceCodeLines = codeEditor.getSourceCode().lines(false);

            if (method.Name.regEx("<.*init*.>").isFalse())
            {
                for (int i = 0; i < 10; i++)
                {
                    if (lineNumber > i && sourceCodeLines.size() > lineNumber - i)
                    {
                        var line = sourceCodeLines[lineNumber - i];
                        if (sourceCodeLines[lineNumber - i].contains(method.Name) &&
                            line.regEx("public|private|internal|protected"))
                        {
                            lineNumber = lineNumber - i + 1;
                            break;
                        }
                    }
                }
            }
            codeEditor.gotoLine(lineNumber, 4);

            return(codeEditor);
        }
		public static SourceCodeEditor showInCodeEditor(this SourceCodeEditor codeEditor ,Java_Class _class, Java_Method method)
		{										
			//var _class = classes_bySignature[classSignature];
			var file = _class.file();
			codeEditor.open(file);
			var lineNumber = 0; 
			if (method.isNull() ||method.LineNumbers.isNull())
				return codeEditor;
			foreach(var item in method.LineNumbers)
				if (item.Line_Number > 1)
					if (lineNumber == 0 || item.Line_Number < lineNumber)
						lineNumber = item.Line_Number;
						
			//this to match the method name to the location (vs the first method)
			var sourceCodeLines = codeEditor.getSourceCode().lines(false);
			if (method.Name.regEx("<.*init*.>").isFalse())
			{
				for(int i=0 ; i < 10 ; i++)
				{
					
					if (lineNumber > i &&   sourceCodeLines.size() > lineNumber-i)
					{
						var line = sourceCodeLines[lineNumber-i];					
						if (sourceCodeLines[lineNumber-i].contains(method.Name) &&
							line.regEx("public|private|internal|protected"))
						{						
							lineNumber = lineNumber -i + 1;						
							break;
						}
					}
				}
			}			
			codeEditor.gotoLine(lineNumber,4);
			
			return codeEditor;
   		}