diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index aad11b68..df41a432 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -48,7 +48,6 @@ import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IModuleDescription; -import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; @@ -70,6 +69,8 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.microsoft.jdtls.ext.core.parser.ContextResolver; +import com.microsoft.jdtls.ext.core.parser.ContextResolver.ImportClassInfo; import com.microsoft.jdtls.ext.core.model.PackageNode; public final class ProjectCommand { @@ -86,15 +87,7 @@ public MainClassInfo(String name, String path) { } } - private static class ImportClassInfo { - public String uri; - public String className; - public ImportClassInfo(String uri, String className) { - this.uri = uri; - this.className = className; - } - } private static class Classpath { public String source; @@ -348,9 +341,17 @@ public static boolean checkImportStatus() { return hasError; } - public static ImportClassInfo[] getImportClassContent(List arguments, IProgressMonitor monitor) { + /** + * Get import class content for Copilot integration. + * This method extracts information about imported classes from a Java file. + * + * @param arguments List containing the file URI as the first element + * @param monitor Progress monitor for cancellation support + * @return List of ImportClassInfo containing class information and JavaDoc + */ + public static List getImportClassContent(List arguments, IProgressMonitor monitor) { if (arguments == null || arguments.isEmpty()) { - return new ImportClassInfo[0]; + return Collections.emptyList(); } try { @@ -360,7 +361,7 @@ public static ImportClassInfo[] getImportClassContent(List arguments, IP java.net.URI uri = new java.net.URI(fileUri); String filePath = uri.getPath(); if (filePath == null) { - return new ImportClassInfo[0]; + return Collections.emptyList(); } IPath path = new Path(filePath); @@ -369,25 +370,26 @@ public static ImportClassInfo[] getImportClassContent(List arguments, IP IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IFile file = root.getFileForLocation(path); if (file == null || !file.exists()) { - return new ImportClassInfo[0]; + return Collections.emptyList(); } // Get the Java project IJavaProject javaProject = JavaCore.create(file.getProject()); if (javaProject == null || !javaProject.exists()) { - return new ImportClassInfo[0]; + return Collections.emptyList(); } // Find the compilation unit IJavaElement javaElement = JavaCore.create(file); if (!(javaElement instanceof org.eclipse.jdt.core.ICompilationUnit)) { - return new ImportClassInfo[0]; + return Collections.emptyList(); } org.eclipse.jdt.core.ICompilationUnit compilationUnit = (org.eclipse.jdt.core.ICompilationUnit) javaElement; // Parse imports and resolve local project files - List result = new ArrayList<>(); + // Delegate to JavaContentParser for processing + List classInfoList = new ArrayList<>(); // Get all imports from the compilation unit org.eclipse.jdt.core.IImportDeclaration[] imports = compilationUnit.getImports(); @@ -402,412 +404,24 @@ public static ImportClassInfo[] getImportClassContent(List arguments, IP boolean isStatic = (importDecl.getFlags() & org.eclipse.jdt.core.Flags.AccStatic) != 0; if (isStatic) { - // Handle static imports - resolveStaticImport(javaProject, importName, result, processedTypes); + // Handle static imports - delegate to ContextResolver + ContextResolver.resolveStaticImport(javaProject, importName, classInfoList, processedTypes, monitor); } else if (importName.endsWith(".*")) { - // Handle package imports + // Handle package imports - delegate to ContextResolver String packageName = importName.substring(0, importName.length() - 2); - resolvePackageTypes(javaProject, packageName, result, processedTypes); + ContextResolver.resolvePackageTypes(javaProject, packageName, classInfoList, processedTypes, monitor); } else { - // Handle single type imports - resolveSingleType(javaProject, importName, result, processedTypes); - } - } - - return result.toArray(new ImportClassInfo[0]); - - } catch (Exception e) { - JdtlsExtActivator.logException("Error in resolveCopilotRequest", e); - return new ImportClassInfo[0]; - } - } - - private static void resolveSingleType(IJavaProject javaProject, String typeName, List result, - Set processedTypes) { - try { - if (processedTypes.contains(typeName)) { - return; - } - processedTypes.add(typeName); - - // Extract package and simple name from the fully qualified type name - int lastDotIndex = typeName.lastIndexOf('.'); - if (lastDotIndex == -1) { - // Default package or invalid type name - return; - } - - String packageName = typeName.substring(0, lastDotIndex); - String simpleName = typeName.substring(lastDotIndex + 1); - - // Strategy: Use JDT's global type resolution first (comprehensive), - // then fallback to manual package fragment traversal if needed - - // Primary path: Use JDT's findType which searches all sources and dependencies - try { - org.eclipse.jdt.core.IType type = javaProject.findType(typeName); - if (type != null && type.exists()) { - // Found type - check if it's a source type we want to process - if (!type.isBinary()) { - // Source type found - extract information and return - extractTypeInfo(type, result); - return; - } - // Note: Binary types (from JARs/JRE) are intentionally ignored - // as they don't provide useful context for code completion - } - } catch (JavaModelException e) { - JdtlsExtActivator.logException("Error in primary type search: " + typeName, e); - // Continue to fallback method - } - - // Fallback path: Manual search in local source package fragments - // This is used when findType() doesn't return results or fails - IPackageFragmentRoot[] packageRoots = javaProject.getPackageFragmentRoots(); - for (IPackageFragmentRoot packageRoot : packageRoots) { - if (packageRoot.getKind() == IPackageFragmentRoot.K_SOURCE) { - org.eclipse.jdt.core.IPackageFragment packageFragment = packageRoot.getPackageFragment(packageName); - if (packageFragment != null && packageFragment.exists()) { - // Look for compilation unit with matching name - org.eclipse.jdt.core.ICompilationUnit cu = packageFragment.getCompilationUnit(simpleName + ".java"); - if (cu != null && cu.exists() && cu.getResource() != null && cu.getResource().exists()) { - // Get primary type from compilation unit - org.eclipse.jdt.core.IType primaryType = cu.findPrimaryType(); - if (primaryType != null && primaryType.exists() && - typeName.equals(primaryType.getFullyQualifiedName())) { - // Found local project source type via fallback method - extractTypeInfo(primaryType, result); - return; - } - - // Also check for inner types in the compilation unit - org.eclipse.jdt.core.IType[] allTypes = cu.getAllTypes(); - for (org.eclipse.jdt.core.IType type : allTypes) { - if (typeName.equals(type.getFullyQualifiedName())) { - extractTypeInfo(type, result); - return; - } - } - } - } - } - } - } catch (JavaModelException e) { - // Log but continue processing other types - JdtlsExtActivator.logException("Error resolving type: " + typeName, e); - } - } - - private static void resolveStaticImport(IJavaProject javaProject, String staticImportName, List result, - Set processedTypes) { - try { - if (staticImportName.endsWith(".*")) { - // Static import of all static members from a class: import static MyClass.*; - String className = staticImportName.substring(0, staticImportName.length() - 2); - resolveStaticMembersFromClass(javaProject, className, result, processedTypes); - } else { - // Static import of specific member: import static MyClass.myMethod; - int lastDotIndex = staticImportName.lastIndexOf('.'); - if (lastDotIndex > 0) { - String className = staticImportName.substring(0, lastDotIndex); - String memberName = staticImportName.substring(lastDotIndex + 1); - resolveStaticMemberFromClass(javaProject, className, memberName, result, processedTypes); - } - } - } catch (Exception e) { - JdtlsExtActivator.logException("Error resolving static import: " + staticImportName, e); - } - } - - private static void resolveStaticMembersFromClass(IJavaProject javaProject, String className, - List result, Set processedTypes) { - try { - // First resolve the class itself to get context information - resolveSingleType(javaProject, className, result, processedTypes); - - // Find the type and extract its static members - org.eclipse.jdt.core.IType type = javaProject.findType(className); - if (type != null && type.exists() && !type.isBinary()) { - String staticMemberInfo = "staticImport:" + className + ".*"; - - // Add information about available static members - List staticMembers = new ArrayList<>(); - - // Get static methods - IMethod[] methods = type.getMethods(); - for (IMethod method : methods) { - int flags = method.getFlags(); - if (org.eclipse.jdt.core.Flags.isStatic(flags) && org.eclipse.jdt.core.Flags.isPublic(flags)) { - staticMembers.add("method:" + method.getElementName() + getParameterTypes(method)); - } - } - - // Get static fields - org.eclipse.jdt.core.IField[] fields = type.getFields(); - for (org.eclipse.jdt.core.IField field : fields) { - int flags = field.getFlags(); - if (org.eclipse.jdt.core.Flags.isStatic(flags) && org.eclipse.jdt.core.Flags.isPublic(flags)) { - staticMembers.add("field:" + field.getElementName()); - } - } - - if (!staticMembers.isEmpty()) { - staticMemberInfo += "|members:" + String.join(",", staticMembers.subList(0, Math.min(staticMembers.size(), 10))); - } - - String uri = getTypeUri(type); - if (uri != null) { - result.add(new ImportClassInfo(uri, staticMemberInfo)); - } - } - } catch (JavaModelException e) { - JdtlsExtActivator.logException("Error resolving static members from: " + className, e); - } - } - - private static void resolveStaticMemberFromClass(IJavaProject javaProject, String className, String memberName, - List result, Set processedTypes) { - try { - // First resolve the class itself - resolveSingleType(javaProject, className, result, processedTypes); - - // Find the specific static member - org.eclipse.jdt.core.IType type = javaProject.findType(className); - if (type != null && type.exists() && !type.isBinary()) { - String memberInfo = "staticImport:" + className + "." + memberName; - - // Check if it's a method - IMethod[] methods = type.getMethods(); - for (IMethod method : methods) { - if (method.getElementName().equals(memberName)) { - int flags = method.getFlags(); - if (org.eclipse.jdt.core.Flags.isStatic(flags)) { - memberInfo += "|type:method" + getParameterTypes(method); - break; - } - } - } - - // Check if it's a field - org.eclipse.jdt.core.IField[] fields = type.getFields(); - for (org.eclipse.jdt.core.IField field : fields) { - if (field.getElementName().equals(memberName)) { - int flags = field.getFlags(); - if (org.eclipse.jdt.core.Flags.isStatic(flags)) { - memberInfo += "|type:field"; - break; - } - } + // Handle single type imports - delegate to ContextResolver + ContextResolver.resolveSingleType(javaProject, importName, classInfoList, processedTypes, monitor); } - - String uri = getTypeUri(type); - if (uri != null) { - result.add(new ImportClassInfo(uri, memberInfo)); - } - } - } catch (JavaModelException e) { - JdtlsExtActivator.logException("Error resolving static member: " + className + "." + memberName, e); - } - } - - private static void resolvePackageTypes(IJavaProject javaProject, String packageName, List result, - Set processedTypes) { - try { - // Find all package fragments with this name - IPackageFragmentRoot[] packageRoots = javaProject.getPackageFragmentRoots(); - for (IPackageFragmentRoot packageRoot : packageRoots) { - if (packageRoot.getKind() == IPackageFragmentRoot.K_SOURCE) { - org.eclipse.jdt.core.IPackageFragment packageFragment = packageRoot.getPackageFragment(packageName); - if (packageFragment != null && packageFragment.exists()) { - // Get all compilation units in this package - org.eclipse.jdt.core.ICompilationUnit[] compilationUnits = packageFragment - .getCompilationUnits(); - for (org.eclipse.jdt.core.ICompilationUnit cu : compilationUnits) { - // Get all types in the compilation unit - org.eclipse.jdt.core.IType[] types = cu.getAllTypes(); - for (org.eclipse.jdt.core.IType type : types) { - String fullTypeName = type.getFullyQualifiedName(); - if (!processedTypes.contains(fullTypeName)) { - processedTypes.add(fullTypeName); - extractTypeInfo(type, result); - } - } - } - } - } - } - } catch (JavaModelException e) { - // Log but continue processing - JdtlsExtActivator.logException("Error resolving package: " + packageName, e); - } - } - - private static void extractTypeInfo(org.eclipse.jdt.core.IType type, List result) { - try { - String typeName = type.getFullyQualifiedName(); - String typeInfo = ""; - - // Determine type kind - if (type.isInterface()) { - typeInfo = "interface:" + typeName; - } else if (type.isClass()) { - extractDetailedClassInfo(type, result); - return; // extractDetailedClassInfo handles adding to result - } else if (type.isEnum()) { - typeInfo = "enum:" + typeName; - } else if (type.isAnnotation()) { - typeInfo = "annotation:" + typeName; - } else { - typeInfo = "type:" + typeName; - } - - // Get URI for this type - String uri = getTypeUri(type); - if (uri != null) { - result.add(new ImportClassInfo(uri, typeInfo)); - } - - // Also add nested types - org.eclipse.jdt.core.IType[] nestedTypes = type.getTypes(); - for (org.eclipse.jdt.core.IType nestedType : nestedTypes) { - extractTypeInfo(nestedType, result); - } - - } catch (JavaModelException e) { - // Log but continue processing other types - JdtlsExtActivator.logException("Error extracting type info for: " + type.getElementName(), e); - } - } - - private static void extractDetailedClassInfo(org.eclipse.jdt.core.IType type, List result) { - try { - if (!type.isClass()) { - return; // Only process classes - } - - String className = type.getFullyQualifiedName(); - List classDetails = new ArrayList<>(); - - // 1. Class declaration information - classDetails.add("class:" + className); - - // 2. Modifiers - int flags = type.getFlags(); - List modifiers = new ArrayList<>(); - if (org.eclipse.jdt.core.Flags.isPublic(flags)) - modifiers.add("public"); - if (org.eclipse.jdt.core.Flags.isAbstract(flags)) - modifiers.add("abstract"); - if (org.eclipse.jdt.core.Flags.isFinal(flags)) - modifiers.add("final"); - if (org.eclipse.jdt.core.Flags.isStatic(flags)) - modifiers.add("static"); - if (!modifiers.isEmpty()) { - classDetails.add("modifiers:" + String.join(",", modifiers)); - } - - // 3. Inheritance - String superclass = type.getSuperclassName(); - if (superclass != null && !"Object".equals(superclass)) { - classDetails.add("extends:" + superclass); } - // 4. Implemented interfaces - String[] interfaces = type.getSuperInterfaceNames(); - if (interfaces.length > 0) { - classDetails.add("implements:" + String.join(",", interfaces)); - } - - // 5. Constructors - IMethod[] methods = type.getMethods(); - List constructors = new ArrayList<>(); - List publicMethods = new ArrayList<>(); - - for (IMethod method : methods) { - if (method.isConstructor()) { - constructors.add(method.getElementName() + getParameterTypes(method)); - } else if (org.eclipse.jdt.core.Flags.isPublic(method.getFlags())) { - publicMethods.add(method.getElementName() + getParameterTypes(method)); - } - } - - if (!constructors.isEmpty()) { - classDetails.add("constructors:" + String.join(",", constructors)); - } + return classInfoList; - if (!publicMethods.isEmpty()) { - classDetails.add("publicMethods:" - + String.join(",", publicMethods.subList(0, Math.min(publicMethods.size(), 10)))); - } - - // 6. Public fields - org.eclipse.jdt.core.IField[] fields = type.getFields(); - List publicFields = new ArrayList<>(); - for (org.eclipse.jdt.core.IField field : fields) { - if (org.eclipse.jdt.core.Flags.isPublic(field.getFlags())) { - publicFields.add(field.getElementName()); - } - } - - if (!publicFields.isEmpty()) { - classDetails.add("publicFields:" + String.join(",", publicFields)); - } - - // Get URI for this type - String uri = getTypeUri(type); - if (uri != null) { - // Combine all information into one string - String classInfo = String.join("|", classDetails); - result.add(new ImportClassInfo(uri, classInfo)); - } - - } catch (JavaModelException e) { - JdtlsExtActivator.logException("Error extracting detailed class info", e); - } - } - - // Helper method: Get file URI/path for the type (instead of fully qualified class name) - private static String getTypeUri(org.eclipse.jdt.core.IType type) { - try { - // Get the compilation unit that contains this type - org.eclipse.jdt.core.ICompilationUnit compilationUnit = type.getCompilationUnit(); - if (compilationUnit != null) { - // Get the underlying resource (file) - org.eclipse.core.resources.IResource resource = compilationUnit.getUnderlyingResource(); - if (resource != null && resource instanceof org.eclipse.core.resources.IFile) { - org.eclipse.core.resources.IFile file = (org.eclipse.core.resources.IFile) resource; - // Get the file location as a file URI - java.net.URI fileUri = file.getLocationURI(); - if (fileUri != null) { - return fileUri.toString(); - } - - // Fallback: use workspace-relative path as URI - return file.getFullPath().toString(); - } - } - - // Fallback: if we can't get file URI, return the fully qualified class name - // This should rarely happen for source types - return type.getFullyQualifiedName(); } catch (Exception e) { - JdtlsExtActivator.logException("Error getting file URI for type: " + type.getElementName(), e); - // Fallback to class name in case of error - try { - return type.getFullyQualifiedName(); - } catch (Exception e2) { - return null; - } - } - } - - // Helper method: Get method parameter types - private static String getParameterTypes(IMethod method) { - String[] paramTypes = method.getParameterTypes(); - if (paramTypes.length == 0) { - return "()"; + JdtlsExtActivator.logException("Error in getImportClassContent", e); + return Collections.emptyList(); } - return "(" + String.join(",", paramTypes) + ")"; } private static void reportExportJarMessage(String terminalId, int severity, String message) { @@ -870,4 +484,4 @@ public boolean isBelongsToWorkspace() { return belongsToWorkspace; } } -} \ No newline at end of file +} diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/parser/ContextResolver.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/parser/ContextResolver.java new file mode 100644 index 00000000..e0218b0c --- /dev/null +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/parser/ContextResolver.java @@ -0,0 +1,1094 @@ +/******************************************************************************* + * Copyright (c) 2018 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ + +package com.microsoft.jdtls.ext.core.parser; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaModelException; + +import com.microsoft.jdtls.ext.core.JdtlsExtActivator; + +/** + * Parser for extracting Java class content information for Copilot integration. + * Handles import resolution, JavaDoc extraction, and class description generation. + */ +public class ContextResolver { + + // Pre-compiled regex patterns for performance + private static final Pattern MARKDOWN_CODE_PATTERN = Pattern.compile("(?s)```(?:java)?\\n?(.*?)```"); + private static final Pattern HTML_PRE_PATTERN = Pattern.compile("(?is)]*>(.*?)"); + private static final Pattern HTML_CODE_PATTERN = Pattern.compile("(?is)]*>(.*?)"); + + // Constants for limiting displayed members + private static final int MAX_METHODS_TO_DISPLAY = 10; + private static final int MAX_FIELDS_TO_DISPLAY = 10; + private static final int MAX_STATIC_METHODS_TO_DISPLAY = 10; + private static final int MAX_STATIC_FIELDS_TO_DISPLAY = 10; + + /** + * ImportClassInfo - Conforms to Copilot CodeSnippet format + * Used to provide Java class context information and JavaDoc to Copilot + */ + public static class ImportClassInfo { + public String uri; // File URI (required) + public String className; // Human-readable class description with JavaDoc appended (required) + + public ImportClassInfo(String uri, String className) { + this.uri = uri; + this.className = className; + } + } + + /** + * Resolve a single type import and extract its information + */ + public static void resolveSingleType(IJavaProject javaProject, String typeName, List classInfoList, + Set processedTypes, IProgressMonitor monitor) { + try { + if (processedTypes.contains(typeName)) { + return; + } + processedTypes.add(typeName); + + // Extract package and simple name from the fully qualified type name + int lastDotIndex = typeName.lastIndexOf('.'); + if (lastDotIndex == -1) { + // Default package or invalid type name + return; + } + + String packageName = typeName.substring(0, lastDotIndex); + String simpleName = typeName.substring(lastDotIndex + 1); + + // Strategy: Use JDT's global type resolution first (comprehensive), + // then fallback to manual package fragment traversal if needed + + // Primary path: Use JDT's findType which searches all sources and dependencies + try { + org.eclipse.jdt.core.IType type = javaProject.findType(typeName); + if (type != null && type.exists()) { + // Found type - check if it's a source type we want to process + if (!type.isBinary()) { + // Source type found - extract information and return + extractTypeInfo(type, classInfoList, monitor); + return; + } + // Note: Binary types (from JARs/JRE) are intentionally ignored + // as they don't provide useful context for code completion + } + } catch (JavaModelException e) { + JdtlsExtActivator.logException("Error in primary type search: " + typeName, e); + // Continue to fallback method + } + + // Fallback path: Manual search in local source package fragments + // This is used when findType() doesn't return results or fails + IPackageFragmentRoot[] packageRoots = javaProject.getPackageFragmentRoots(); + for (IPackageFragmentRoot packageRoot : packageRoots) { + if (packageRoot.getKind() == IPackageFragmentRoot.K_SOURCE) { + org.eclipse.jdt.core.IPackageFragment packageFragment = packageRoot.getPackageFragment(packageName); + if (packageFragment != null && packageFragment.exists()) { + // Look for compilation unit with matching name + org.eclipse.jdt.core.ICompilationUnit cu = packageFragment.getCompilationUnit(simpleName + ".java"); + if (cu != null && cu.exists() && cu.getResource() != null && cu.getResource().exists()) { + // Get primary type from compilation unit + org.eclipse.jdt.core.IType primaryType = cu.findPrimaryType(); + if (primaryType != null && primaryType.exists() && + typeName.equals(primaryType.getFullyQualifiedName())) { + // Found local project source type via fallback method + extractTypeInfo(primaryType, classInfoList, monitor); + return; + } + + // Also check for inner types in the compilation unit + org.eclipse.jdt.core.IType[] allTypes = cu.getAllTypes(); + for (org.eclipse.jdt.core.IType type : allTypes) { + if (typeName.equals(type.getFullyQualifiedName())) { + extractTypeInfo(type, classInfoList, monitor); + return; + } + } + } + } + } + } + } catch (JavaModelException e) { + // Log but continue processing other types + JdtlsExtActivator.logException("Error resolving type: " + typeName, e); + } + } + + /** + * Resolve a static import statement + */ + public static void resolveStaticImport(IJavaProject javaProject, String staticImportName, List classInfoList, + Set processedTypes, IProgressMonitor monitor) { + try { + if (staticImportName.endsWith(".*")) { + // Static import of all static members from a class: import static MyClass.*; + String className = staticImportName.substring(0, staticImportName.length() - 2); + resolveStaticMembersFromClass(javaProject, className, classInfoList, processedTypes, monitor); + } else { + // Static import of specific member: import static MyClass.myMethod; + int lastDotIndex = staticImportName.lastIndexOf('.'); + if (lastDotIndex > 0) { + String className = staticImportName.substring(0, lastDotIndex); + String memberName = staticImportName.substring(lastDotIndex + 1); + resolveStaticMemberFromClass(javaProject, className, memberName, classInfoList, processedTypes, monitor); + } + } + } catch (Exception e) { + JdtlsExtActivator.logException("Error resolving static import: " + staticImportName, e); + } + } + + /** + * Resolve all static members from a class + */ + public static void resolveStaticMembersFromClass(IJavaProject javaProject, String className, + List classInfoList, Set processedTypes, IProgressMonitor monitor) { + try { + // First resolve the class itself to get context information + resolveSingleType(javaProject, className, classInfoList, processedTypes, monitor); + + // Find the type and extract its static members + org.eclipse.jdt.core.IType type = javaProject.findType(className); + if (type != null && type.exists() && !type.isBinary()) { + StringBuilder description = new StringBuilder(); + description.append("Static Import: ").append(className).append(".*\n"); + description.append("All static members from ").append(className).append("\n\n"); + + // Get static methods + IMethod[] methods = type.getMethods(); + List staticMethodSigs = new ArrayList<>(); + for (IMethod method : methods) { + int flags = method.getFlags(); + if (org.eclipse.jdt.core.Flags.isStatic(flags) && org.eclipse.jdt.core.Flags.isPublic(flags)) { + if (staticMethodSigs.size() < MAX_STATIC_METHODS_TO_DISPLAY) { + staticMethodSigs.add(generateMethodSignature(method)); + } + } + } + + // Get static fields + org.eclipse.jdt.core.IField[] fields = type.getFields(); + List staticFieldSigs = new ArrayList<>(); + for (org.eclipse.jdt.core.IField field : fields) { + int flags = field.getFlags(); + if (org.eclipse.jdt.core.Flags.isStatic(flags) && org.eclipse.jdt.core.Flags.isPublic(flags)) { + if (staticFieldSigs.size() < MAX_STATIC_FIELDS_TO_DISPLAY) { + staticFieldSigs.add(generateFieldSignature(field)); + } + } + } + + if (!staticMethodSigs.isEmpty()) { + description.append("Static Methods:\n"); + for (String sig : staticMethodSigs) { + description.append(" - ").append(sig).append("\n"); + } + description.append("\n"); + } + + if (!staticFieldSigs.isEmpty()) { + description.append("Static Fields:\n"); + for (String sig : staticFieldSigs) { + description.append(" - ").append(sig).append("\n"); + } + } + + String uri = getTypeUri(type); + if (uri != null) { + classInfoList.add(new ImportClassInfo(uri, description.toString())); + } + } + } catch (JavaModelException e) { + JdtlsExtActivator.logException("Error resolving static members from: " + className, e); + } + } + + /** + * Resolve a specific static member from a class + */ + public static void resolveStaticMemberFromClass(IJavaProject javaProject, String className, String memberName, + List classInfoList, Set processedTypes, IProgressMonitor monitor) { + try { + // First resolve the class itself + resolveSingleType(javaProject, className, classInfoList, processedTypes, monitor); + + // Find the specific static member + org.eclipse.jdt.core.IType type = javaProject.findType(className); + if (type != null && type.exists() && !type.isBinary()) { + StringBuilder description = new StringBuilder(); + description.append("Static Import: ").append(className).append(".").append(memberName).append("\n\n"); + + boolean found = false; + + // Check if it's a method + IMethod[] methods = type.getMethods(); + for (IMethod method : methods) { + if (method.getElementName().equals(memberName)) { + int flags = method.getFlags(); + if (org.eclipse.jdt.core.Flags.isStatic(flags)) { + description.append("Static Method:\n"); + description.append(" - ").append(generateMethodSignature(method)).append("\n"); + found = true; + break; + } + } + } + + // Check if it's a field + if (!found) { + org.eclipse.jdt.core.IField[] fields = type.getFields(); + for (org.eclipse.jdt.core.IField field : fields) { + if (field.getElementName().equals(memberName)) { + int flags = field.getFlags(); + if (org.eclipse.jdt.core.Flags.isStatic(flags)) { + description.append("Static Field:\n"); + description.append(" - ").append(generateFieldSignature(field)).append("\n"); + found = true; + break; + } + } + } + } + + if (found) { + String uri = getTypeUri(type); + if (uri != null) { + classInfoList.add(new ImportClassInfo(uri, description.toString())); + } + } + } + } catch (JavaModelException e) { + JdtlsExtActivator.logException("Error resolving static member: " + className + "." + memberName, e); + } + } + + /** + * Resolve all types in a package (for wildcard imports) + */ + public static void resolvePackageTypes(IJavaProject javaProject, String packageName, List classInfoList, + Set processedTypes, IProgressMonitor monitor) { + try { + // Find all package fragments with this name + IPackageFragmentRoot[] packageRoots = javaProject.getPackageFragmentRoots(); + for (IPackageFragmentRoot packageRoot : packageRoots) { + if (packageRoot.getKind() == IPackageFragmentRoot.K_SOURCE) { + org.eclipse.jdt.core.IPackageFragment packageFragment = packageRoot.getPackageFragment(packageName); + if (packageFragment != null && packageFragment.exists()) { + // Get all compilation units in this package + org.eclipse.jdt.core.ICompilationUnit[] compilationUnits = packageFragment + .getCompilationUnits(); + for (org.eclipse.jdt.core.ICompilationUnit cu : compilationUnits) { + // Get all types in the compilation unit + org.eclipse.jdt.core.IType[] types = cu.getAllTypes(); + for (org.eclipse.jdt.core.IType type : types) { + String fullTypeName = type.getFullyQualifiedName(); + if (!processedTypes.contains(fullTypeName)) { + processedTypes.add(fullTypeName); + extractTypeInfo(type, classInfoList, monitor); + } + } + } + } + } + } + } catch (JavaModelException e) { + // Log but continue processing + JdtlsExtActivator.logException("Error resolving package: " + packageName, e); + } + } + + /** + * Extract type information and generate ImportClassInfo conforming to Copilot CodeSnippet format + * Also extracts JavaDoc if available and appends it to the class description + * Improved version: generates human-readable class descriptions with integrated JavaDoc + */ + public static void extractTypeInfo(org.eclipse.jdt.core.IType type, List classInfoList, + IProgressMonitor monitor) { + try { + // Get file URI + String uri = getTypeUri(type); + if (uri == null) { + return; + } + + // Extract relevant JavaDoc content first (code snippets with fallback strategy) + // This uses a hybrid approach: AST extraction -> HTML extraction -> Markdown extraction -> fallback + String relevantJavadoc = extractRelevantJavaDocContent(type, monitor); + + // Generate human-readable class description with JavaDoc inserted after signature + String description = generateClassDescription(type, relevantJavadoc); + + // Create ImportClassInfo (conforms to Copilot CodeSnippet format) + ImportClassInfo info = new ImportClassInfo(uri, description); + classInfoList.add(info); + + // Recursively process nested types + org.eclipse.jdt.core.IType[] nestedTypes = type.getTypes(); + for (org.eclipse.jdt.core.IType nestedType : nestedTypes) { + extractTypeInfo(nestedType, classInfoList, monitor); + } + + } catch (JavaModelException e) { + JdtlsExtActivator.logException("Error extracting type info for: " + type.getElementName(), e); + } + } + + /** + * Get file URI/path for the type (instead of fully qualified class name) + */ + public static String getTypeUri(org.eclipse.jdt.core.IType type) { + try { + // Get the compilation unit that contains this type + org.eclipse.jdt.core.ICompilationUnit compilationUnit = type.getCompilationUnit(); + if (compilationUnit != null) { + // Get the underlying resource (file) + org.eclipse.core.resources.IResource resource = compilationUnit.getUnderlyingResource(); + if (resource != null && resource instanceof org.eclipse.core.resources.IFile) { + org.eclipse.core.resources.IFile file = (org.eclipse.core.resources.IFile) resource; + // Get the file location as a file URI + java.net.URI fileUri = file.getLocationURI(); + if (fileUri != null) { + return fileUri.toString(); + } + + // Fallback: use workspace-relative path as URI + return file.getFullPath().toString(); + } + } + + // Fallback: if we can't get file URI, return the fully qualified class name + // This should rarely happen for source types + return type.getFullyQualifiedName(); + } catch (Exception e) { + JdtlsExtActivator.logException("Error getting file URI for type: " + type.getElementName(), e); + // Fallback to class name in case of error + try { + return type.getFullyQualifiedName(); + } catch (Exception e2) { + return null; + } + } + } + + /** + * Generate complete class description (natural language format, similar to JavaDoc) + * @param type the Java type to describe + * @param javadoc optional JavaDoc content to insert after signature (can be null or empty) + */ + public static String generateClassDescription(org.eclipse.jdt.core.IType type, String javadoc) { + StringBuilder description = new StringBuilder(); + + try { + String qualifiedName = type.getFullyQualifiedName(); + String simpleName = type.getElementName(); + + // === 1. Title and signature === + description.append("Class: ").append(qualifiedName).append("\n"); + + // Generate class signature + StringBuilder signature = new StringBuilder(); + int flags = type.getFlags(); + + if (org.eclipse.jdt.core.Flags.isPublic(flags)) signature.append("public "); + if (org.eclipse.jdt.core.Flags.isAbstract(flags)) signature.append("abstract "); + if (org.eclipse.jdt.core.Flags.isFinal(flags)) signature.append("final "); + + if (type.isInterface()) { + signature.append("interface "); + } else if (type.isEnum()) { + signature.append("enum "); + } else if (type.isAnnotation()) { + signature.append("@interface "); + } else { + signature.append("class "); + } + + signature.append(simpleName); + + // Type parameters + String[] typeParams = type.getTypeParameterSignatures(); + if (typeParams != null && typeParams.length > 0) { + signature.append("<"); + for (int i = 0; i < typeParams.length; i++) { + if (i > 0) signature.append(", "); + signature.append(convertTypeSignature(typeParams[i])); + } + signature.append(">"); + } + + // Inheritance relationship + String superclass = type.getSuperclassName(); + if (superclass != null && !superclass.equals("Object") && !type.isInterface()) { + signature.append(" extends ").append(superclass); + } + + // Implemented interfaces + String[] interfaces = type.getSuperInterfaceNames(); + if (interfaces != null && interfaces.length > 0) { + if (type.isInterface()) { + signature.append(" extends "); + } else { + signature.append(" implements "); + } + for (int i = 0; i < interfaces.length; i++) { + if (i > 0) signature.append(", "); + signature.append(interfaces[i]); + } + } + + description.append("Signature: ").append(signature).append("\n\n"); + + // === 2. JavaDoc (inserted after signature) === + if (isNotEmpty(javadoc)) { + description.append("JavaDoc:\n").append(javadoc).append("\n\n"); + } + + // === 3. Constructors === + IMethod[] methods = type.getMethods(); + List constructorSigs = new ArrayList<>(); + + for (IMethod method : methods) { + if (method.isConstructor()) { + constructorSigs.add(generateMethodSignature(method)); + } + } + + if (!constructorSigs.isEmpty()) { + description.append("Constructors:\n"); + for (String sig : constructorSigs) { + description.append(" - ").append(sig).append("\n"); + } + description.append("\n"); + } + + // === 4. Public methods (limited to first 10) === + List methodSigs = new ArrayList<>(); + int methodCount = 0; + + for (IMethod method : methods) { + if (!method.isConstructor() && org.eclipse.jdt.core.Flags.isPublic(method.getFlags())) { + if (methodCount < MAX_METHODS_TO_DISPLAY) { + methodSigs.add(generateMethodSignature(method)); + methodCount++; + } else { + break; + } + } + } + + if (!methodSigs.isEmpty()) { + description.append("Methods:\n"); + for (String sig : methodSigs) { + description.append(" - ").append(sig).append("\n"); + } + if (methodCount == MAX_METHODS_TO_DISPLAY && methods.length > methodCount) { + description.append(" - ... (more methods available)\n"); + } + description.append("\n"); + } + + // === 5. Public fields (limited to first 10) === + org.eclipse.jdt.core.IField[] fields = type.getFields(); + List fieldSigs = new ArrayList<>(); + int fieldCount = 0; + + for (org.eclipse.jdt.core.IField field : fields) { + if (org.eclipse.jdt.core.Flags.isPublic(field.getFlags()) && fieldCount < MAX_FIELDS_TO_DISPLAY) { + fieldSigs.add(generateFieldSignature(field)); + fieldCount++; + } + } + + if (!fieldSigs.isEmpty()) { + description.append("Fields:\n"); + for (String sig : fieldSigs) { + description.append(" - ").append(sig).append("\n"); + } + } + + } catch (JavaModelException e) { + return "Error generating description for type: " + e.getMessage(); + } + + return description.toString(); + } + + // ================ JavaDoc Extraction Methods ================ + + /** + * Extracts relevant code snippets from Javadoc. + * This method is optimized to extract code from `` tags and markdown code fences, + * and formats them in an LLM-readable format. + * + * @param type the type to extract Javadoc from. + * @param monitor the progress monitor. + * @return A string containing all found code snippets, formatted as markdown code blocks. + */ + private static String extractRelevantJavaDocContent(org.eclipse.jdt.core.IType type, IProgressMonitor monitor) { + try { + String rawJavadoc; + boolean isHtml; + + if (type.isBinary()) { + rawJavadoc = type.getAttachedJavadoc(monitor); + isHtml = true; + } else { + org.eclipse.jdt.core.ISourceRange javadocRange = type.getJavadocRange(); + if (javadocRange == null) { + return ""; + } + rawJavadoc = type.getCompilationUnit().getSource().substring(javadocRange.getOffset(), javadocRange.getOffset() + javadocRange.getLength()); + isHtml = false; // Javadoc comment from source is not HTML + } + + if (!isNotEmpty(rawJavadoc)) { + return ""; + } + + StringBuilder allCodeSnippets = new StringBuilder(); + Set seenCodeSnippets = new HashSet<>(); + + // 1. Extract markdown code blocks (```...```) + Matcher markdownMatcher = MARKDOWN_CODE_PATTERN.matcher(rawJavadoc); + while (markdownMatcher.find()) { + String code = markdownMatcher.group(1).trim(); + if (isNotEmpty(code) && seenCodeSnippets.add(code)) { + allCodeSnippets.append("```java\n").append(code).append("\n```\n\n"); + } + } + + // 2. Extract HTML
 and  blocks
+            // Clean Javadoc comment for HTML extraction
+            String cleanedForHtml = isHtml ? rawJavadoc : cleanJavadocComment(rawJavadoc);
+            cleanedForHtml = convertHtmlEntities(cleanedForHtml);
+
+            // Priority 1: 
 blocks (often contain well-formatted code)
+            Matcher preMatcher = HTML_PRE_PATTERN.matcher(cleanedForHtml);
+            while (preMatcher.find()) {
+                String code = preMatcher.group(1).replaceAll("(?i)]*>", "").replaceAll("(?i)", "").trim();
+                if (isNotEmpty(code) && seenCodeSnippets.add(code)) {
+                    allCodeSnippets.append("```java\n").append(code).append("\n```\n\n");
+                }
+            }
+
+            // Priority 2:  blocks (for inline snippets)
+            Matcher codeMatcher = HTML_CODE_PATTERN.matcher(cleanedForHtml);
+            while (codeMatcher.find()) {
+                String code = codeMatcher.group(1).trim();
+                // Use HashSet for O(1) duplicate checking
+                if (isNotEmpty(code) && seenCodeSnippets.add(code)) {
+                    allCodeSnippets.append("```java\n").append(code).append("\n```\n\n");
+                }
+            }
+
+            return allCodeSnippets.toString().trim();
+
+        } catch (Exception e) {
+            JdtlsExtActivator.logException("Error extracting relevant JavaDoc content for: " + type.getElementName(), e);
+            return "";
+        }
+    }
+
+    /**
+     * Clean up raw JavaDoc comment by removing comment markers and asterisks
+     */
+    private static String cleanJavadocComment(String rawJavadoc) {
+        if (rawJavadoc == null || rawJavadoc.isEmpty()) {
+            return "";
+        }
+        
+        // Remove opening /** and closing */
+        String cleaned = rawJavadoc;
+        cleaned = cleaned.replaceFirst("^/\\*\\*", "");
+        cleaned = cleaned.replaceFirst("\\*/$", "");
+        
+        // Split into lines and clean each line
+        String[] lines = cleaned.split("\\r?\\n");
+        StringBuilder result = new StringBuilder();
+        
+        for (String line : lines) {
+            // Remove leading whitespace and asterisk
+            String trimmed = line.trim();
+            if (trimmed.startsWith("*")) {
+                trimmed = trimmed.substring(1).trim();
+            }
+            
+            // Skip empty lines at the beginning
+            if (result.length() == 0 && trimmed.isEmpty()) {
+                continue;
+            }
+            
+            // Add line to result
+            if (result.length() > 0 && !trimmed.isEmpty()) {
+                result.append("\n");
+            }
+            result.append(trimmed);
+        }
+        
+        return result.toString();
+    }
+
+
+    /**
+     * Convert HTML entities to their plain text equivalents
+     */
+    private static String convertHtmlEntities(String text) {
+        if (text == null || text.isEmpty()) {
+            return text;
+        }
+        String result = text;
+        result = result.replace(" ", " ");
+        result = result.replace("<", "<");
+        result = result.replace(">", ">");
+        result = result.replace("&", "&");
+        result = result.replace(""", "\"");
+        result = result.replace("'", "'");
+        result = result.replace("'", "'");
+        result = result.replace("—", "-");
+        result = result.replace("–", "-");
+        return result;
+    }
+
+    /**
+     * Extract summary description from method JavaDoc
+     * Returns the first sentence or paragraph of the JavaDoc as a brief description
+     */
+    private static String extractMethodJavaDocSummary(IMethod method) {
+        try {
+            // Try to get JavaDoc from source
+            org.eclipse.jdt.core.ISourceRange javadocRange = method.getJavadocRange();
+            if (javadocRange == null) {
+                return "";
+            }
+            
+            String rawJavadoc = method.getCompilationUnit().getSource()
+                .substring(javadocRange.getOffset(), javadocRange.getOffset() + javadocRange.getLength());
+            
+            if (!isNotEmpty(rawJavadoc)) {
+                return "";
+            }
+            
+            // Clean the JavaDoc comment
+            String cleaned = cleanJavadocComment(rawJavadoc);
+            
+            // Extract the description (before any @param, @return, @throws tags)
+            String description = extractJavadocDescription(cleaned);
+            
+            // Get first sentence or limit length
+            String summary = getFirstSentenceOrLimit(description, 120);
+            
+            return summary;
+            
+        } catch (Exception e) {
+            // Silently fail and return empty string
+            return "";
+        }
+    }
+
+    /**
+     * Extract the main description part from JavaDoc (before @tags)
+     */
+    private static String extractJavadocDescription(String cleanedJavadoc) {
+        if (cleanedJavadoc == null || cleanedJavadoc.isEmpty()) {
+            return "";
+        }
+        
+        // Split into lines and extract description before @tags
+        String[] lines = cleanedJavadoc.split("\\n");
+        StringBuilder description = new StringBuilder();
+        
+        for (String line : lines) {
+            String trimmedLine = line.trim();
+            // Check if line starts with @tag
+            if (trimmedLine.startsWith("@")) {
+                break; // Stop at first tag
+            }
+            
+            // Skip empty lines at the beginning
+            if (description.length() == 0 && trimmedLine.isEmpty()) {
+                continue;
+            }
+            
+            if (description.length() > 0) {
+                description.append(" ");
+            }
+            description.append(trimmedLine);
+        }
+        
+        return description.toString().trim();
+    }
+
+    /**
+     * Get the first sentence or limit the text to maxLength characters
+     */
+    private static String getFirstSentenceOrLimit(String text, int maxLength) {
+        if (text == null || text.isEmpty()) {
+            return "";
+        }
+        
+        // Try to find the first sentence (ending with ., !, or ?)
+        int firstPeriod = text.indexOf(". ");
+        int firstExclamation = text.indexOf("! ");
+        int firstQuestion = text.indexOf("? ");
+        
+        int firstSentenceEnd = -1;
+        if (firstPeriod != -1) firstSentenceEnd = firstPeriod;
+        if (firstExclamation != -1 && (firstSentenceEnd == -1 || firstExclamation < firstSentenceEnd)) {
+            firstSentenceEnd = firstExclamation;
+        }
+        if (firstQuestion != -1 && (firstSentenceEnd == -1 || firstQuestion < firstSentenceEnd)) {
+            firstSentenceEnd = firstQuestion;
+        }
+        
+        // If we found a sentence ending and it's within reasonable length
+        if (firstSentenceEnd != -1 && firstSentenceEnd < maxLength) {
+            return text.substring(0, firstSentenceEnd + 1).trim();
+        }
+        
+        // Otherwise, limit to maxLength
+        if (text.length() > maxLength) {
+            // Try to cut at a word boundary
+            int lastSpace = text.lastIndexOf(' ', maxLength);
+            if (lastSpace > maxLength / 2) {
+                return text.substring(0, lastSpace).trim() + "...";
+            }
+            return text.substring(0, maxLength).trim() + "...";
+        }
+        
+        return text.trim();
+    }
+
+    /**
+     * Extract summary description from field JavaDoc
+     */
+    private static String extractFieldJavaDocSummary(org.eclipse.jdt.core.IField field) {
+        try {
+            // Try to get JavaDoc from source
+            org.eclipse.jdt.core.ISourceRange javadocRange = field.getJavadocRange();
+            if (javadocRange == null) {
+                return "";
+            }
+            
+            String rawJavadoc = field.getCompilationUnit().getSource()
+                .substring(javadocRange.getOffset(), javadocRange.getOffset() + javadocRange.getLength());
+            
+            if (!isNotEmpty(rawJavadoc)) {
+                return "";
+            }
+            
+            // Clean the JavaDoc comment
+            String cleaned = cleanJavadocComment(rawJavadoc);
+            
+            // Extract the description (before any @tags)
+            String description = extractJavadocDescription(cleaned);
+            
+            // Get first sentence or limit length
+            String summary = getFirstSentenceOrLimit(description, 120);
+            
+            return summary;
+            
+        } catch (Exception e) {
+            // Silently fail and return empty string
+            return "";
+        }
+    }
+
+    /**
+     * Generate human-readable method signature with JavaDoc description
+     */
+    public static String generateMethodSignature(IMethod method) {
+        StringBuilder sb = new StringBuilder();
+        
+        try {
+            int flags = method.getFlags();
+            appendAccessModifiers(sb, flags);
+            appendOtherModifiers(sb, flags, true);
+            
+            // Type parameters (if any)
+            String[] typeParameters = method.getTypeParameterSignatures();
+            if (typeParameters != null && typeParameters.length > 0) {
+                sb.append("<");
+                for (int i = 0; i < typeParameters.length; i++) {
+                    if (i > 0) sb.append(", ");
+                    sb.append(convertTypeSignature(typeParameters[i]));
+                }
+                sb.append("> ");
+            }
+            
+            // Return type (constructors don't have return type)
+            if (!method.isConstructor()) {
+                String returnType = convertTypeSignature(method.getReturnType());
+                sb.append(returnType).append(" ");
+            }
+            
+            // Method name
+            sb.append(method.getElementName()).append("(");
+            
+            // Parameter list
+            String[] paramTypes = method.getParameterTypes();
+            String[] paramNames = method.getParameterNames();
+            for (int i = 0; i < paramTypes.length; i++) {
+                if (i > 0) {
+                    sb.append(", ");
+                }
+                sb.append(convertTypeSignature(paramTypes[i]));
+                if (paramNames != null && i < paramNames.length) {
+                    sb.append(" ").append(paramNames[i]);
+                }
+            }
+            
+            sb.append(")");
+            
+            // Exception declarations
+            String[] exceptionTypes = method.getExceptionTypes();
+            if (exceptionTypes != null && exceptionTypes.length > 0) {
+                sb.append(" throws ");
+                for (int i = 0; i < exceptionTypes.length; i++) {
+                    if (i > 0) sb.append(", ");
+                    sb.append(convertTypeSignature(exceptionTypes[i]));
+                }
+            }
+            
+        } catch (JavaModelException e) {
+            return method.getElementName() + "(...)";
+        }
+        
+        // Extract JavaDoc description and prepend if exists
+        String javadocSummary = extractMethodJavaDocSummary(method);
+        if (isNotEmpty(javadocSummary)) {
+            return "// " + javadocSummary + "\n      " + sb.toString();
+        }
+        
+        return sb.toString();
+    }
+
+    /**
+     * Generate human-readable field signature with JavaDoc description
+     */
+    public static String generateFieldSignature(org.eclipse.jdt.core.IField field) {
+        StringBuilder sb = new StringBuilder();
+        
+        try {
+            int flags = field.getFlags();
+            appendAccessModifiers(sb, flags);
+            appendOtherModifiers(sb, flags, false);
+            
+            // Type and name
+            String fieldType = convertTypeSignature(field.getTypeSignature());
+            sb.append(fieldType).append(" ").append(field.getElementName());
+            
+            // If it's a constant, try to get the initial value
+            if (org.eclipse.jdt.core.Flags.isStatic(flags) && org.eclipse.jdt.core.Flags.isFinal(flags)) {
+                Object constant = field.getConstant();
+                if (constant != null) {
+                    sb.append(" = ");
+                    if (constant instanceof String) {
+                        sb.append("\"").append(constant).append("\"");
+                    } else {
+                        sb.append(constant);
+                    }
+                }
+            }
+            
+        } catch (JavaModelException e) {
+            return field.getElementName();
+        }
+        
+        // Extract JavaDoc description and prepend if exists
+        String javadocSummary = extractFieldJavaDocSummary(field);
+        if (isNotEmpty(javadocSummary)) {
+            return "// " + javadocSummary + "\n      " + sb.toString();
+        }
+        
+        return sb.toString();
+    }
+
+    /**
+     * Append access modifiers (public/protected/private) to StringBuilder
+     */
+    private static void appendAccessModifiers(StringBuilder sb, int flags) {
+        if (org.eclipse.jdt.core.Flags.isPublic(flags)) {
+            sb.append("public ");
+        } else if (org.eclipse.jdt.core.Flags.isProtected(flags)) {
+            sb.append("protected ");
+        } else if (org.eclipse.jdt.core.Flags.isPrivate(flags)) {
+            sb.append("private ");
+        }
+    }
+
+    /**
+     * Append other modifiers (static/final/abstract) to StringBuilder
+     */
+    private static void appendOtherModifiers(StringBuilder sb, int flags, boolean isMethod) {
+        if (org.eclipse.jdt.core.Flags.isStatic(flags)) {
+            sb.append("static ");
+        }
+        if (org.eclipse.jdt.core.Flags.isFinal(flags)) {
+            sb.append("final ");
+        }
+        if (isMethod && org.eclipse.jdt.core.Flags.isAbstract(flags)) {
+            sb.append("abstract ");
+        }
+    }
+
+    /**
+     * Convert JDT type signature to human-readable format
+     */
+    public static String convertTypeSignature(String jdtSignature) {
+        if (jdtSignature == null || jdtSignature.isEmpty()) {
+            return "void";
+        }
+
+        // Handle array types
+        int arrayDimensions = 0;
+        while (jdtSignature.startsWith("[")) {
+            arrayDimensions++;
+            jdtSignature = jdtSignature.substring(1);
+        }
+
+        String baseType;
+
+        // Handle type parameters and reference types (starts with Q)
+        if (jdtSignature.startsWith("Q") && jdtSignature.endsWith(";")) {
+            baseType = jdtSignature.substring(1, jdtSignature.length() - 1);
+            baseType = baseType.replace('/', '.');
+            
+            // Handle generic type parameters (e.g., "QResult;")
+            baseType = processGenericTypes(baseType);
+            baseType = simplifyTypeName(baseType);
+        }
+        // Handle fully qualified types (starts with L)
+        else if (jdtSignature.startsWith("L") && jdtSignature.endsWith(";")) {
+            baseType = jdtSignature.substring(1, jdtSignature.length() - 1);
+            baseType = baseType.replace('/', '.');
+            
+            // Handle generic type parameters
+            baseType = processGenericTypes(baseType);
+            baseType = simplifyTypeName(baseType);
+        }
+        // Handle primitive types
+        else {
+            switch (jdtSignature.charAt(0)) {
+                case 'I': baseType = "int"; break;
+                case 'Z': baseType = "boolean"; break;
+                case 'V': baseType = "void"; break;
+                case 'J': baseType = "long"; break;
+                case 'F': baseType = "float"; break;
+                case 'D': baseType = "double"; break;
+                case 'B': baseType = "byte"; break;
+                case 'C': baseType = "char"; break;
+                case 'S': baseType = "short"; break;
+                default: baseType = jdtSignature;
+            }
+        }
+
+        // Add array markers
+        for (int i = 0; i < arrayDimensions; i++) {
+            baseType += "[]";
+        }
+
+        return baseType;
+    }
+    
+    /**
+     * Process generic type parameters in a type name
+     * Example: "Result" -> "Result"
+     */
+    private static String processGenericTypes(String typeName) {
+        if (typeName == null || !typeName.contains("<")) {
+            return typeName;
+        }
+        
+        StringBuilder result = new StringBuilder();
+        int i = 0;
+        
+        while (i < typeName.length()) {
+            char c = typeName.charAt(i);
+            
+            if (c == '<' || c == ',' || c == ' ') {
+                // Keep angle brackets, commas, and spaces
+                result.append(c);
+                i++;
+                
+                // Skip whitespace after comma or opening bracket
+                while (i < typeName.length() && typeName.charAt(i) == ' ') {
+                    result.append(' ');
+                    i++;
+                }
+                
+                // Check if next is a type parameter (Q or L prefix)
+                if (i < typeName.length()) {
+                    char next = typeName.charAt(i);
+                    
+                    if (next == 'Q' || next == 'L') {
+                        // Find the end of this type parameter (marked by ;)
+                        int endIndex = typeName.indexOf(';', i);
+                        if (endIndex != -1) {
+                            // Extract the type parameter and convert it
+                            String typeParam = typeName.substring(i + 1, endIndex);
+                            
+                            // Recursively process nested generics
+                            typeParam = processGenericTypes(typeParam);
+                            typeParam = simplifyTypeName(typeParam);
+                            
+                            result.append(typeParam);
+                            i = endIndex + 1; // Skip past the semicolon
+                        } else {
+                            result.append(next);
+                            i++;
+                        }
+                    } else {
+                        // Not a type parameter, just append
+                        result.append(next);
+                        i++;
+                    }
+                }
+            } else {
+                result.append(c);
+                i++;
+            }
+        }
+        
+        return result.toString();
+    }
+
+    /**
+     * Simplify fully qualified type name to just the simple name
+     */
+    private static String simplifyTypeName(String qualifiedName) {
+        if (qualifiedName == null) {
+            return qualifiedName;
+        }
+        int lastDot = qualifiedName.lastIndexOf('.');
+        return lastDot == -1 ? qualifiedName : qualifiedName.substring(lastDot + 1);
+    }
+
+    /**
+     * Utility method to check if a string is not empty or null
+     */
+    private static boolean isNotEmpty(String value) {
+        return value != null && !value.isEmpty();
+    }
+}