astCustomization);
- ClassCustomization refreshSymbol() {
- return new PackageCustomization(editor, languageClient, packageName).getClass(className);
- }
+ ClassCustomization refreshSymbol();
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/CodeCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/CodeCustomization.java
index b8d08e0cfaa..e417e25d9d1 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/CodeCustomization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/CodeCustomization.java
@@ -4,69 +4,52 @@
package com.microsoft.typespec.http.client.generator.core.customization;
import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import com.microsoft.typespec.http.client.generator.core.extension.plugin.JavaSettings;
import org.eclipse.lsp4j.SymbolInformation;
/**
- * Base class for all code based customizations.
+ * Base interface for all code based customizations.
*/
-public abstract class CodeCustomization {
- final Editor editor;
- final EclipseLanguageClient languageClient;
- final SymbolInformation symbol;
- final String fileUri;
- final String fileName;
-
- CodeCustomization(Editor editor, EclipseLanguageClient languageClient, SymbolInformation symbol) {
- this.editor = editor;
- this.languageClient = languageClient;
- this.symbol = symbol;
- this.fileUri = symbol.getLocation().getUri();
- int i = fileUri.toString().indexOf("src/main/java/");
- this.fileName = fileUri.toString().substring(i);
- }
+public interface CodeCustomization {
/**
* The Editor managing the state of the CodeCustomization.
*
* @return The Editor.
*/
- public final Editor getEditor() {
- return editor;
- }
+ Editor getEditor();
/**
* The EclipseLanguageClient managing validation of the CodeCustomization.
+ *
+ * If {@link JavaSettings#isUseEclipseLanguageServer()} returns true, an {@link EclipseLanguageClient} instance will
+ * be returned, otherwise this returns null.
*
* @return The EclipseLanguageClient.
*/
- public final EclipseLanguageClient getLanguageClient() {
- return languageClient;
- }
+ EclipseLanguageClient getLanguageClient();
/**
* The SymbolInformation managing information about the CodeCustomization.
+ *
+ * If {@link JavaSettings#isUseEclipseLanguageServer()} returns true, a {@link SymbolInformation} instance will be
+ * returned, otherwise this returns null.
*
* @return The SymbolInformation.
*/
- public final SymbolInformation getSymbol() {
- return symbol;
- }
+ SymbolInformation getSymbol();
/**
* The URI of the file containing where the code for the CodeCustomization exists.
*
* @return The URI of the file.
*/
- public final String getFileUri() {
- return fileUri;
- }
+ String getFileUri();
/**
* The name of the file containing where the code for the CodeCustomization exists.
*
* @return The name of the file.
*/
- public final String getFileName() {
- return fileName;
- }
+ String getFileName();
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/ConstantCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/ConstantCustomization.java
index 0f1a47369eb..51db409c5b8 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/ConstantCustomization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/ConstantCustomization.java
@@ -3,65 +3,34 @@
package com.microsoft.typespec.http.client.generator.core.customization;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.regex.Pattern;
-import org.eclipse.lsp4j.SymbolInformation;
-import org.eclipse.lsp4j.SymbolKind;
-import org.eclipse.lsp4j.WorkspaceEdit;
/**
* Customization for an AutoRest generated constant property.
*
* For instance property customizations use {@link PropertyCustomization}.
*/
-public final class ConstantCustomization extends CodeCustomization {
- private static final Pattern METHOD_PARAMS_CAPTURE = Pattern.compile("\\(.*\\)");
-
- private final String packageName;
- private final String className;
- private final String constantName;
-
- ConstantCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName, String className,
- SymbolInformation symbol, String constantName) {
- super(editor, languageClient, symbol);
-
- this.packageName = packageName;
- this.className = className;
- this.constantName = constantName;
- }
-
+public interface ConstantCustomization extends CodeCustomization {
/**
* Gets the name of the class that contains this constant.
*
* @return The name of the class that contains this constant.
*/
- public String getClassName() {
- return className;
- }
+ String getClassName();
/**
* Gets the name of this constant.
*
* @return The name of this constant.
*/
- public String getConstantName() {
- return constantName;
- }
+ String getConstantName();
/**
* Gets the Javadoc customization for this constant.
*
* @return The Javadoc customization.
*/
- public JavadocCustomization getJavadoc() {
- return new JavadocCustomization(editor, languageClient, fileUri, fileName,
- symbol.getLocation().getRange().getStart().getLine());
- }
+ JavadocCustomization getJavadoc();
/**
* Replace the modifier for this constant.
@@ -79,12 +48,7 @@ public JavadocCustomization getJavadoc() {
* @throws IllegalArgumentException If the {@code modifier} is less than to {@code 0} or any {@link Modifier}
* included in the bitwise OR isn't a valid constant {@link Modifier}.
*/
- public ConstantCustomization setModifier(int modifiers) {
- Utils.replaceModifier(symbol, editor, languageClient, "(?:.+ )?(\\w+ )" + constantName + "\\(",
- "$1" + constantName + "(", Modifier.fieldModifiers(), Modifier.STATIC | Modifier.FINAL | modifiers);
-
- return refreshCustomization(constantName);
- }
+ ConstantCustomization setModifier(int modifiers);
/**
* Renames the constant.
@@ -99,54 +63,7 @@ public ConstantCustomization setModifier(int modifiers) {
* @return A new instance of {@link ConstantCustomization} for chaining.
* @throws NullPointerException If {@code newName} is null.
*/
- public ConstantCustomization rename(String newName) {
- Objects.requireNonNull(newName, "'newName' cannot be null.");
-
- String lowercaseConstantName = constantName.toLowerCase();
- String currentCamelName = constantToMethodName(constantName);
- String lowercaseCurrentCamelName = currentCamelName.toLowerCase();
- String newCamelName = constantToMethodName(newName);
-
- List edits = new ArrayList<>();
- for (SymbolInformation si : languageClient.listDocumentSymbols(fileUri)) {
- String symbolName = si.getName().toLowerCase();
- if (!symbolName.contains(lowercaseConstantName) && !symbolName.contains(lowercaseCurrentCamelName)) {
- continue;
- }
-
- if (si.getKind() == SymbolKind.Constant) {
- edits.add(languageClient.renameSymbol(fileUri, si.getLocation().getRange().getStart(), newName));
- } else if (si.getKind() == SymbolKind.Method) {
- String methodName = si.getName().replace(currentCamelName, newCamelName).replace(constantName, newName);
- methodName = METHOD_PARAMS_CAPTURE.matcher(methodName).replaceFirst("");
- edits.add(languageClient.renameSymbol(fileUri, si.getLocation().getRange().getStart(), methodName));
- }
- }
-
- Utils.applyWorkspaceEdits(edits, editor, languageClient);
- return refreshCustomization(newName);
- }
-
- private static String constantToMethodName(String constantName) {
- // Constants will be in the form A_WORD_SPLIT_BY_UNDERSCORE_AND_CAPITALIZED, which, if used as-is won't follow
- // getter, or method, naming conventions of getAWordInCamelCase.
- //
- // Split the constant name on '_' and lower case all characters after the first.
- StringBuilder camelBuilder = new StringBuilder(constantName.length());
-
- for (String word : constantName.split("_")) {
- if (word.isEmpty()) {
- continue;
- }
-
- camelBuilder.append(word.charAt(0));
- if (word.length() > 1) {
- camelBuilder.append(word.substring(1).toLowerCase());
- }
- }
-
- return camelBuilder.toString();
- }
+ ConstantCustomization rename(String newName);
/**
* Add an annotation to a property in the class.
@@ -154,9 +71,7 @@ private static String constantToMethodName(String constantName) {
* @param annotation the annotation to add. The leading @ can be omitted.
* @return A new instance of {@link ConstantCustomization} for chaining.
*/
- public ConstantCustomization addAnnotation(String annotation) {
- return Utils.addAnnotation(annotation, this, () -> refreshCustomization(constantName));
- }
+ ConstantCustomization addAnnotation(String annotation);
/**
* Remove an annotation from the constant.
@@ -164,18 +79,5 @@ public ConstantCustomization addAnnotation(String annotation) {
* @param annotation the annotation to remove from the constant. The leading @ can be omitted.
* @return A new instance of {@link ConstantCustomization} for chaining.
*/
- public ConstantCustomization removeAnnotation(String annotation) {
- return Utils.removeAnnotation(this,
- compilationUnit -> compilationUnit.getClassByName(className)
- .get()
- .getFieldByName(constantName)
- .get()
- .getAnnotationByName(Utils.cleanAnnotationName(annotation)),
- () -> refreshCustomization(constantName));
- }
-
- private ConstantCustomization refreshCustomization(String constantName) {
- return new PackageCustomization(editor, languageClient, packageName).getClass(className)
- .getConstant(constantName);
- }
+ ConstantCustomization removeAnnotation(String annotation);
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/ConstructorCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/ConstructorCustomization.java
index a6b94644799..d89109c4091 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/ConstructorCustomization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/ConstructorCustomization.java
@@ -3,46 +3,26 @@
package com.microsoft.typespec.http.client.generator.core.customization;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
import java.lang.reflect.Modifier;
import java.util.List;
-import org.eclipse.lsp4j.SymbolInformation;
/**
* The constructor level customization for an AutoRest generated constructor.
*/
-public final class ConstructorCustomization extends CodeCustomization {
- private final String packageName;
- private final String className;
- private final String constructorSignature;
-
- ConstructorCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName, String className,
- String constructorSignature, SymbolInformation symbol) {
- super(editor, languageClient, symbol);
- this.packageName = packageName;
- this.className = className;
- this.constructorSignature = constructorSignature;
- }
-
+public interface ConstructorCustomization extends CodeCustomization {
/**
* Gets the name of the class containing the constructor.
*
* @return The name of the class containing the constructor.
*/
- public String getClassName() {
- return className;
- }
+ String getClassName();
/**
* Gets the Javadoc customization for this constructor.
*
* @return The Javadoc customization for this constructor.
*/
- public JavadocCustomization getJavadoc() {
- return new JavadocCustomization(editor, languageClient, fileUri, fileName,
- symbol.getLocation().getRange().getStart().getLine());
- }
+ JavadocCustomization getJavadoc();
/**
* Add an annotation to the constructor.
@@ -50,9 +30,7 @@ public JavadocCustomization getJavadoc() {
* @param annotation The annotation to add to the constructor. The leading @ can be omitted.
* @return A new ConstructorCustomization representing the updated constructor.
*/
- public ConstructorCustomization addAnnotation(String annotation) {
- return Utils.addAnnotation(annotation, this, () -> refreshCustomization(constructorSignature));
- }
+ ConstructorCustomization addAnnotation(String annotation);
/**
* Remove an annotation from the constructor.
@@ -60,18 +38,7 @@ public ConstructorCustomization addAnnotation(String annotation) {
* @param annotation The annotation to remove from the constructor. The leading @ can be omitted.
* @return A new ConstructorCustomization representing the updated constructor.
*/
- public ConstructorCustomization removeAnnotation(String annotation) {
- return Utils.removeAnnotation(this,
- compilationUnit -> compilationUnit.getClassByName(className)
- .get()
- .getConstructors()
- .stream()
- .filter(ctor -> Utils.declarationContainsSymbol(ctor.getRange().get(), symbol.getLocation().getRange()))
- .findFirst()
- .get()
- .getAnnotationByName(Utils.cleanAnnotationName(annotation)),
- () -> refreshCustomization(constructorSignature));
- }
+ ConstructorCustomization removeAnnotation(String annotation);
/**
* Replace the modifier for this constructor.
@@ -86,12 +53,7 @@ public ConstructorCustomization removeAnnotation(String annotation) {
* @throws IllegalArgumentException If the {@code modifier} is less than to {@code 0} or any {@link Modifier}
* included in the bitwise OR isn't a valid constructor {@link Modifier}.
*/
- public ConstructorCustomization setModifier(int modifiers) {
- Utils.replaceModifier(symbol, editor, languageClient, "(?:.+ )?" + className + "\\(", className + "(",
- Modifier.constructorModifiers(), modifiers);
-
- return refreshCustomization(constructorSignature);
- }
+ ConstructorCustomization setModifier(int modifiers);
/**
* Replace the parameters of the constructor.
@@ -99,9 +61,7 @@ public ConstructorCustomization setModifier(int modifiers) {
* @param newParameters New constructor parameters.
* @return A new ConstructorCustomization representing the updated constructor.
*/
- public ConstructorCustomization replaceParameters(String newParameters) {
- return replaceParameters(newParameters, null);
- }
+ ConstructorCustomization replaceParameters(String newParameters);
/**
* Replaces the parameters of the constructor and adds any additional imports required by the new parameters.
@@ -111,18 +71,7 @@ public ConstructorCustomization replaceParameters(String newParameters) {
* are ambiguous on which to use such as {@code List} or the utility class {@code Arrays}.
* @return A new ConstructorCustomization representing the updated constructor.
*/
- public ConstructorCustomization replaceParameters(String newParameters, List importsToAdd) {
- String newSignature = className + "(" + newParameters + ")";
-
- ClassCustomization classCustomization
- = new PackageCustomization(editor, languageClient, packageName).getClass(className);
-
- ClassCustomization updatedClassCustomization
- = Utils.addImports(importsToAdd, classCustomization, classCustomization::refreshSymbol);
-
- return Utils.replaceParameters(newParameters, updatedClassCustomization.getConstructor(constructorSignature),
- () -> updatedClassCustomization.getConstructor(newSignature));
- }
+ ConstructorCustomization replaceParameters(String newParameters, List importsToAdd);
/**
* Replace the body of the constructor.
@@ -130,9 +79,7 @@ public ConstructorCustomization replaceParameters(String newParameters, List importsToAdd) {
- ClassCustomization classCustomization
- = new PackageCustomization(editor, languageClient, packageName).getClass(className);
-
- ClassCustomization updatedClassCustomization
- = Utils.addImports(importsToAdd, classCustomization, classCustomization::refreshSymbol);
-
- return Utils.replaceBody(newBody, updatedClassCustomization.getConstructor(constructorSignature),
- () -> updatedClassCustomization.getConstructor(constructorSignature));
- }
-
- private ConstructorCustomization refreshCustomization(String constructorSignature) {
- return new PackageCustomization(editor, languageClient, packageName).getClass(className)
- .getConstructor(constructorSignature);
- }
+ ConstructorCustomization replaceBody(String newBody, List importsToAdd);
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/Customization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/Customization.java
index 04d3b99d599..c9529d5854b 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/Customization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/Customization.java
@@ -4,6 +4,8 @@
package com.microsoft.typespec.http.client.generator.core.customization;
import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization.EclipseLibraryCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.javaparsercustomization.JavaParserLibraryCustomization;
import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
import com.microsoft.typespec.http.client.generator.core.extension.base.util.FileUtils;
import java.io.IOException;
@@ -21,10 +23,11 @@ public abstract class Customization {
* Start the customization process. This is called by the post processor in AutoRest.
*
* @param files the map of files generated in the previous steps in AutoRest
+ * @param useEclipseLanguageServer whether to use the Eclipse language server
* @param logger the logger
* @return the map of files after customization
*/
- public final Map run(Map files, Logger logger) {
+ public final Map run(Map files, boolean useEclipseLanguageServer, Logger logger) {
Path tempDirWithPrefix;
// Populate editor
@@ -32,25 +35,35 @@ public final Map run(Map files, Logger logger) {
try {
tempDirWithPrefix = FileUtils.createTempDirectory("temp");
editor = new Editor(files, tempDirWithPrefix);
- InputStream pomStream = Customization.class.getResourceAsStream("/pom.xml");
- byte[] buffer = new byte[pomStream.available()];
- pomStream.read(buffer);
- editor.addFile("pom.xml", new String(buffer, StandardCharsets.UTF_8));
+ if (useEclipseLanguageServer) {
+ try (InputStream pomStream = Customization.class.getResourceAsStream("/pom.xml")) {
+ editor.addFile("pom.xml", new String(pomStream.readAllBytes(), StandardCharsets.UTF_8));
+ }
+ }
} catch (IOException e) {
throw new RuntimeException(e);
}
- // Start language client
- try (EclipseLanguageClient languageClient
- = new EclipseLanguageClient(null, tempDirWithPrefix.toString(), logger)) {
- languageClient.initialize();
- customize(new LibraryCustomization(editor, languageClient), logger);
- editor.removeFile("pom.xml");
- return editor.getContents();
- } catch (Exception e) {
- throw new RuntimeException(e);
- } finally {
- Utils.deleteDirectory(tempDirWithPrefix.toFile());
+ if (useEclipseLanguageServer) {
+ // Start language client
+ try (EclipseLanguageClient languageClient
+ = new EclipseLanguageClient(null, tempDirWithPrefix.toString(), logger)) {
+ languageClient.initialize();
+ customize(new EclipseLibraryCustomization(editor, languageClient), logger);
+ editor.removeFile("pom.xml");
+ return editor.getContents();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ Utils.deleteDirectory(tempDirWithPrefix.toFile());
+ }
+ } else {
+ try {
+ customize(new JavaParserLibraryCustomization(editor), logger);
+ return editor.getContents();
+ } finally {
+ Utils.deleteDirectory(tempDirWithPrefix.toFile());
+ }
}
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/Editor.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/Editor.java
index 4cbca279812..36a708a7340 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/Editor.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/Editor.java
@@ -44,7 +44,44 @@ public Editor(Map contents, Path rootDir) {
for (Map.Entry content : contents.entrySet()) {
addFile(content.getKey(), content.getValue());
}
+ }
+
+ /**
+ * Checks if the package exists in the editor.
+ *
+ * @param packageName the package name
+ * @return Whether the package exists
+ */
+ public boolean packageExists(String packageName) {
+ String toFind = "src/main/java/" + packageName.replace('.', '/') + "/";
+ return contents.keySet().stream().anyMatch(fileName -> fileName.startsWith(toFind));
+ }
+
+ /**
+ * Checks if a class exists in the editor.
+ *
+ * @param packageName the package name of the class
+ * @param className the class name
+ * @return Whether the class exists
+ */
+ public boolean classExists(String packageName, String className) {
+ String fileName = "src/main/java/" + packageName.replace('.', '/') + "/" + className + ".java";
+ return contents.containsKey(fileName);
+ }
+ /**
+ * Lists all classes in a package.
+ *
+ * @param packageName the package name
+ * @return the list of classes in the package
+ */
+ public List classesInPackage(String packageName) {
+ String packagePath = "src/main/java/" + packageName.replace(".", "/") + "/";
+ return contents.keySet()
+ .stream()
+ .filter(fileName -> fileName.startsWith(packagePath))
+ .map(fileName -> fileName.substring(packagePath.length() + 1, fileName.length() - 5))
+ .collect(Collectors.toList());
}
/**
@@ -85,10 +122,7 @@ private void addOrReplaceFile(String name, String content, boolean isReplace) {
try {
boolean fileCreated = newFile.createNewFile();
-
- try (BufferedWriter writer = Files.newBufferedWriter(newFile.toPath())) {
- writer.write(content);
- }
+ Files.writeString(newFile.toPath(), content);
if (fileCreated || isReplace) {
contents.put(name, content);
@@ -198,7 +232,7 @@ public void replaceWithIndentedContent(String fileName, Position start, Position
List replacementLineContent = splitContentIntoLines(newContent);
// Add the change.
- if (replacementLineContent.size() > 0) {
+ if (!replacementLineContent.isEmpty()) {
for (int i = 0; i != replacementLineContent.size() - 1; i++) {
if (i > 0) {
stringBuilder.append(indent);
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/JavadocCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/JavadocCustomization.java
index aea96a9ea73..cfaeafe03bd 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/JavadocCustomization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/JavadocCustomization.java
@@ -3,117 +3,35 @@
package com.microsoft.typespec.http.client.generator.core.customization;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.eclipse.lsp4j.FileChangeType;
-import org.eclipse.lsp4j.FileEvent;
-import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
/**
* The Javadoc customization for an AutoRest generated classes and methods.
*/
-public final class JavadocCustomization {
- /*
- * This pattern attempts to cleanse a line of a Javadoc.
- *
- * The scenarios handled by this pattern are the following:
+public interface JavadocCustomization> {
+ /**
+ * Gets the range of the Javadoc customization.
*
- * 1. A single line Javadoc
- * 2. An indented single line Javadoc
- * 3. A part of a Javadoc
- * 4. An indented part of a Javadoc
- * 5. A Javadoc where the closing line contains text
- * 6. An indented Javadoc where the closing line contains text
+ * @return The range of the Javadoc customization.
*/
- private static final Pattern JAVADOC_LINE_CLEANER = Pattern.compile("^\\s*/?\\*{1,2}\\s?(.*?)(?:\\s*\\*/)?$");
-
- private static final Pattern EMPTY_JAVADOC_LINE_PATTERN = Pattern.compile("\\s*\\*/?\\s*");
-
- private static final Pattern THROWS_TAG = Pattern.compile(".*@throws ");
- private static final Pattern PARAM_TAG = Pattern.compile(".*@param ");
- private static final Pattern SPACE_THEN_ANYTHING = Pattern.compile(" .*");
- private static final Pattern JAVADOC_LINE_WITH_CONTENT = Pattern.compile("\\* .*$");
- private static final Pattern END_JAVADOC_LINE = Pattern.compile(" \\*/$");
- private static final Pattern JAVADOC_CONTENT = Pattern.compile(" +\\* ");
-
- private final EclipseLanguageClient languageClient;
- private final Editor editor;
- private final String fileUri;
- private final String fileName;
- private final String indent;
-
- private String descriptionDocs;
- private final Map paramDocs;
- private String returnDoc;
- private final Map throwsDocs;
- private final List seeDocs;
- private String sinceDoc;
- private String deprecatedDoc;
- private Range javadocRange;
-
- JavadocCustomization(Editor editor, EclipseLanguageClient languageClient, String fileUri, String fileName,
- int symbolLine) {
- this.editor = editor;
- this.languageClient = languageClient;
-
- this.paramDocs = new LinkedHashMap<>();
- this.throwsDocs = new LinkedHashMap<>();
- this.seeDocs = new ArrayList<>();
-
- this.fileUri = fileUri;
- this.fileName = fileName;
-
- this.indent = Utils.getIndent(editor.getFileLine(fileName, symbolLine));
- parseJavadoc(symbolLine);
- }
+ Range getJavadocRange();
- Range getJavadocRange() {
- return javadocRange;
- }
-
- public JavadocCustomization replace(JavadocCustomization other) {
- this.descriptionDocs = other.descriptionDocs;
-
- this.paramDocs.clear();
- if (other.paramDocs != null) {
- this.paramDocs.putAll(other.paramDocs);
- }
-
- this.returnDoc = other.returnDoc;
-
- this.throwsDocs.clear();
- if (other.throwsDocs != null) {
- this.throwsDocs.putAll(other.throwsDocs);
- }
-
- this.seeDocs.clear();
- if (other.seeDocs != null) {
- this.seeDocs.addAll(other.seeDocs);
- }
-
- this.sinceDoc = other.sinceDoc;
- this.deprecatedDoc = other.deprecatedDoc;
- commit();
- return this;
- }
+ /**
+ * Replaces the current Javadoc customization's content with the content of another Javadoc customization.
+ *
+ * @param other The other Javadoc customization to replace with.
+ * @return The updated Javadoc customization object.
+ */
+ T replace(T other);
/**
* Gets the Javadoc description.
*
* @return The Javadoc description.
*/
- public String getDescription() {
- return descriptionDocs;
- }
+ String getDescription();
/**
* Sets the description in the Javadoc.
@@ -121,18 +39,14 @@ public String getDescription() {
* @param description the description for the current class/method.
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization setDescription(String description) {
- return performChange(this.descriptionDocs, description, () -> this.descriptionDocs = description);
- }
+ JavadocCustomization setDescription(String description);
/**
* Gets a read-only view of the Javadoc params.
*
* @return Read-only view of the Javadoc params.
*/
- public Map getParams() {
- return Collections.unmodifiableMap(paramDocs);
- }
+ Map getParams();
/**
* Sets the param Javadoc for a parameter on the method.
@@ -141,10 +55,7 @@ public Map getParams() {
* @param description the description for this parameter
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization setParam(String parameterName, String description) {
- return performChange(paramDocs.get(parameterName), description,
- () -> paramDocs.put(parameterName, description));
- }
+ JavadocCustomization setParam(String parameterName, String description);
/**
* Removes a parameter Javadoc on the method.
@@ -152,20 +63,14 @@ public JavadocCustomization setParam(String parameterName, String description) {
* @param parameterName the name of the parameter on the method
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization removeParam(String parameterName) {
- paramDocs.remove(parameterName);
- commit();
- return this;
- }
+ JavadocCustomization removeParam(String parameterName);
/**
* Gets the Javadoc return.
*
* @return The Javadoc return.
*/
- public String getReturn() {
- return returnDoc;
- }
+ String getReturn();
/**
* Sets the return Javadoc on the method.
@@ -173,27 +78,21 @@ public String getReturn() {
* @param description the description for the return value
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization setReturn(String description) {
- return performChange(returnDoc, description, () -> this.returnDoc = description);
- }
+ JavadocCustomization setReturn(String description);
/**
* Removes the return Javadoc for a method.
*
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization removeReturn() {
- return performChange(returnDoc, null, () -> this.returnDoc = null);
- }
+ JavadocCustomization removeReturn();
/**
* Gets a read-only view of the Javadoc throws.
*
* @return Read-only view of the Javadoc throws.
*/
- public Map getThrows() {
- return Collections.unmodifiableMap(throwsDocs);
- }
+ Map getThrows();
/**
* Adds a throws Javadoc for a method.
@@ -202,10 +101,7 @@ public Map getThrows() {
* @param description the description for the exception
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization addThrows(String exceptionType, String description) {
- return performChange(throwsDocs.get(exceptionType), description,
- () -> throwsDocs.put(exceptionType, description));
- }
+ JavadocCustomization addThrows(String exceptionType, String description);
/**
* Removes a throw Javadoc for a method.
@@ -213,20 +109,14 @@ public JavadocCustomization addThrows(String exceptionType, String description)
* @param exceptionType the type of the exception the method will throw
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization removeThrows(String exceptionType) {
- throwsDocs.remove(exceptionType);
- commit();
- return this;
- }
+ JavadocCustomization removeThrows(String exceptionType);
/**
* Gets a read-only view of the Javadoc sees.
*
* @return Read-only view of the Javadoc sees.
*/
- public List getSees() {
- return Collections.unmodifiableList(seeDocs);
- }
+ List getSees();
/**
* Adds a see Javadoc.
@@ -236,20 +126,14 @@ public List getSees() {
* @see Oracle docs on see
* tag
*/
- public JavadocCustomization addSee(String seeDoc) {
- seeDocs.add(seeDoc);
- commit();
- return this;
- }
+ JavadocCustomization addSee(String seeDoc);
/**
* Gets the Javadoc since.
*
* @return The Javadoc since.
*/
- public String getSince() {
- return sinceDoc;
- }
+ String getSince();
/**
* Sets the since Javadoc on the method.
@@ -257,27 +141,21 @@ public String getSince() {
* @param sinceDoc the version for the since tag
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization setSince(String sinceDoc) {
- return performChange(this.sinceDoc, sinceDoc, () -> this.sinceDoc = sinceDoc);
- }
+ JavadocCustomization setSince(String sinceDoc);
/**
* Removes the Javadoc since.
*
* @return The updated JavadocCustomization object.
*/
- public JavadocCustomization removeSince() {
- return performChange(this.sinceDoc, null, () -> this.sinceDoc = null);
- }
+ JavadocCustomization removeSince();
/**
* Gets the Javadoc deprecated.
*
* @return The Javadoc deprecated.
*/
- public String getDeprecated() {
- return deprecatedDoc;
- }
+ String getDeprecated();
/**
* Sets the deprecated Javadoc on the method.
@@ -285,180 +163,12 @@ public String getDeprecated() {
* @param deprecatedDoc the deprecation reason
* @return the Javadoc customization object for chaining
*/
- public JavadocCustomization setDeprecated(String deprecatedDoc) {
- return performChange(this.deprecatedDoc, deprecatedDoc, () -> this.deprecatedDoc = deprecatedDoc);
- }
+ JavadocCustomization setDeprecated(String deprecatedDoc);
/**
* Removes the Javadoc deprecated.
*
* @return The updated JavadocCustomization object.
*/
- public JavadocCustomization removeDeprecated() {
- return performChange(this.deprecatedDoc, null, () -> this.deprecatedDoc = null);
- }
-
- private void initialize(int symbolLine) {
- editor.insertBlankLine(fileName, symbolLine++, false);
- editor.replace(fileName, new Position(symbolLine, 0), new Position(symbolLine, 0), indent);
- Position javadocCursor = new Position(symbolLine, indent.length());
- javadocRange = new Range(javadocCursor, javadocCursor);
- ++symbolLine;
- FileEvent blankLineEvent = new FileEvent();
- blankLineEvent.setUri(fileUri);
- blankLineEvent.setType(FileChangeType.Changed);
- languageClient.notifyWatchedFilesChanged(Collections.singletonList(blankLineEvent));
- }
-
- private void parseJavadoc(int symbolLine) {
- String lineContent = editor.getFileLine(fileName, --symbolLine);
- while (lineContent.startsWith(indent + "@")) {
- lineContent = editor.getFileLine(fileName, --symbolLine);
- }
- if (lineContent.endsWith("*/")) {
- Position javadocEnd = new Position(symbolLine, lineContent.length());
- int currentDocEndLine = symbolLine;
- while (!lineContent.contains("/*")) {
- if (lineContent.contains("@throws")) {
- String type = THROWS_TAG.matcher(lineContent).replaceFirst("");
- type = SPACE_THEN_ANYTHING.matcher(type).replaceFirst("");
- Position docStart = new Position(symbolLine, lineContent.indexOf("@throws") + 8);
- Position docEnd
- = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
- throwsDocs.put(type, readJavadocTextRange(editor, fileName, docStart, docEnd));
- currentDocEndLine = symbolLine - 1;
- } else if (lineContent.contains("@return")) {
- Position docStart = new Position(symbolLine, lineContent.indexOf("@return") + 8);
- Position docEnd
- = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
- returnDoc = readJavadocTextRange(editor, fileName, docStart, docEnd);
- currentDocEndLine = symbolLine - 1;
- } else if (lineContent.contains("@since")) {
- Position docStart = new Position(symbolLine, lineContent.indexOf("@since") + 7);
- Position docEnd
- = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
- sinceDoc = readJavadocTextRange(editor, fileName, docStart, docEnd);
- currentDocEndLine = symbolLine - 1;
- } else if (lineContent.contains("@see")) {
- Position docStart = new Position(symbolLine, lineContent.indexOf("@see") + 5);
- Position docEnd
- = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
- seeDocs.add(readJavadocTextRange(editor, fileName, docStart, docEnd));
- currentDocEndLine = symbolLine - 1;
- } else if (lineContent.contains("@deprecated")) {
- Position docStart = new Position(symbolLine, lineContent.indexOf("@deprecated") + 5);
- Position docEnd
- = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
- deprecatedDoc = readJavadocTextRange(editor, fileName, docStart, docEnd);
- currentDocEndLine = symbolLine - 1;
- } else if (lineContent.contains("@param")) {
- String name = PARAM_TAG.matcher(lineContent).replaceFirst("");
- name = SPACE_THEN_ANYTHING.matcher(name).replaceFirst("");
- Position docStart = new Position(symbolLine, lineContent.indexOf("@param") + 8 + name.length());
- Position docEnd
- = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
- paramDocs.put(name, readJavadocTextRange(editor, fileName, docStart, docEnd));
- currentDocEndLine = symbolLine - 1;
- } else if (EMPTY_JAVADOC_LINE_PATTERN.matcher(lineContent).matches()) {
- // empty line
- currentDocEndLine--;
- }
- lineContent = editor.getFileLine(fileName, --symbolLine);
- }
- Position javadocStart = new Position(symbolLine, indent.length());
- javadocRange = new Range(javadocStart, javadocEnd);
- if (lineContent.endsWith("/*") || lineContent.endsWith("/**")) {
- symbolLine++;
- }
- Position descriptionStart = new Position(symbolLine,
- JAVADOC_LINE_WITH_CONTENT.matcher(editor.getFileLine(fileName, symbolLine)).replaceFirst("").length()
- + 2);
- String descriptionEndLineContent = editor.getFileLine(fileName, currentDocEndLine);
- while (descriptionEndLineContent.trim().endsWith("*")) {
- descriptionEndLineContent = editor.getFileLine(fileName, --currentDocEndLine);
- }
- Position descriptionEnd = new Position(currentDocEndLine,
- END_JAVADOC_LINE.matcher(descriptionEndLineContent).replaceFirst("").length());
- this.descriptionDocs = JAVADOC_CONTENT
- .matcher(editor.getTextInRange(fileName, new Range(descriptionStart, descriptionEnd), " "))
- .replaceAll(" ")
- .trim();
- } else {
- initialize(symbolLine);
- }
- }
-
- private static String readJavadocTextRange(Editor editor, String fileName, Position docStart, Position docEnd) {
- return editor.getTextInRange(fileName, new Range(docStart, docEnd), " ", line -> {
- Matcher lineCleaningMatch = JAVADOC_LINE_CLEANER.matcher(line);
- return (lineCleaningMatch.find()) ? lineCleaningMatch.group(1) : line;
- }).trim();
- }
-
- private void commit() {
- // Given this method is self-contained use StringBuilder as it doesn't have synchronization.
- // Additional start with a sizeable 4kb buffer to reduce chances of resizing while keeping it small.
- StringBuilder stringBuilder = new StringBuilder(4096);
-
- Utils.writeLine(stringBuilder, "/**");
- if (descriptionDocs != null) {
- Utils.writeLine(stringBuilder.append(indent).append(" * "), descriptionDocs);
- }
-
- if (!paramDocs.isEmpty() || !throwsDocs.isEmpty() || returnDoc != null || deprecatedDoc != null) {
- Utils.writeLine(stringBuilder.append(indent), " * ");
-
- for (Map.Entry paramDoc : paramDocs.entrySet()) {
- Utils.writeLine(stringBuilder.append(indent).append(" * @param ").append(paramDoc.getKey()).append(" "),
- paramDoc.getValue());
- }
-
- if (returnDoc != null) {
- Utils.writeLine(stringBuilder.append(indent).append(" * @return "), returnDoc);
- }
-
- for (Map.Entry throwsDoc : throwsDocs.entrySet()) {
- Utils.writeLine(
- stringBuilder.append(indent).append(" * @throws ").append(throwsDoc.getKey()).append(" "),
- throwsDoc.getValue());
- }
-
- for (String seeDoc : seeDocs) {
- Utils.writeLine(stringBuilder.append(indent).append(" * @see "), seeDoc);
- }
-
- if (sinceDoc != null) {
- Utils.writeLine(stringBuilder.append(indent).append(" * @since "), sinceDoc);
- }
-
- if (deprecatedDoc != null) {
- Utils.writeLine(stringBuilder.append(indent).append(" * @deprecated "), deprecatedDoc);
- }
-
- }
-
- stringBuilder.append(indent).append(" */");
-
- editor.replace(fileName, javadocRange.getStart(), javadocRange.getEnd(), stringBuilder.toString());
- FileEvent replaceEvent = new FileEvent();
- replaceEvent.setUri(fileUri);
- replaceEvent.setType(FileChangeType.Changed);
- languageClient.notifyWatchedFilesChanged(Collections.singletonList(replaceEvent));
-
- int javadocStartLine = javadocRange.getStart().getLine();
- String lineContent = editor.getFileLine(fileName, javadocStartLine);
- while (!lineContent.endsWith("*/")) {
- lineContent = editor.getFileLine(fileName, ++javadocStartLine);
- }
- parseJavadoc(javadocStartLine + 1);
- }
-
- private JavadocCustomization performChange(String oldValue, String newValue, Runnable changePerformer) {
- if (!Objects.equals(oldValue, newValue)) {
- changePerformer.run();
- commit();
- }
-
- return this;
- }
+ JavadocCustomization removeDeprecated();
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/LibraryCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/LibraryCustomization.java
index 88323dd8868..b10b261cbc6 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/LibraryCustomization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/LibraryCustomization.java
@@ -3,32 +3,17 @@
package com.microsoft.typespec.http.client.generator.core.customization;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
-import java.util.Optional;
-import org.eclipse.lsp4j.SymbolInformation;
-
/**
* The top level customization for an AutoRest generated client library.
*/
-public final class LibraryCustomization {
- private final EclipseLanguageClient languageClient;
- private final Editor editor;
-
- LibraryCustomization(Editor editor, EclipseLanguageClient languageClient) {
- this.editor = editor;
- this.languageClient = languageClient;
- }
-
+public interface LibraryCustomization {
/**
* Gets the package level customization for a Java package in the client library.
*
* @param packageName the fully qualified name of the package
* @return the package level customization.
*/
- public PackageCustomization getPackage(String packageName) {
- return new PackageCustomization(editor, languageClient, packageName);
- }
+ PackageCustomization getPackage(String packageName);
/**
* Gets the class level customization for a Java class in the client library.
@@ -37,29 +22,12 @@ public PackageCustomization getPackage(String packageName) {
* @param className the simple name of the class
* @return the class level customization
*/
- public ClassCustomization getClass(String packageName, String className) {
- String packagePath = packageName.replace(".", "/");
- Optional classSymbol = languageClient.findWorkspaceSymbol(className)
- .stream()
- // findWorkspace symbol finds all classes that contain the classname term
- // The filter that checks the filename only works if there are no nested classes
- // So, when customizing client classes that contain service interface, this can incorrectly return
- // the service interface instead of the client class. So, we should add another check for exact name match
- .filter(si -> si.getName().equals(className))
- .filter(si -> si.getLocation().getUri().toString().endsWith(packagePath + "/" + className + ".java"))
- .findFirst();
-
- return Utils.returnIfPresentOrThrow(classSymbol,
- symbol -> new ClassCustomization(editor, languageClient, packageName, className, symbol),
- () -> new IllegalArgumentException(className + " does not exist in package " + packageName));
- }
+ ClassCustomization getClass(String packageName, String className);
/**
* Gets the raw editor containing the current files being edited and eventually emitted to the disk.
*
* @return the raw editor
*/
- public Editor getRawEditor() {
- return editor;
- }
+ Editor getRawEditor();
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/MethodCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/MethodCustomization.java
index 8fad074db8a..9bf475afba1 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/MethodCustomization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/MethodCustomization.java
@@ -3,65 +3,33 @@
package com.microsoft.typespec.http.client.generator.core.customization;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import org.eclipse.lsp4j.FileChangeType;
-import org.eclipse.lsp4j.FileEvent;
-import org.eclipse.lsp4j.Position;
-import org.eclipse.lsp4j.Range;
-import org.eclipse.lsp4j.SymbolInformation;
-import org.eclipse.lsp4j.TextEdit;
-import org.eclipse.lsp4j.WorkspaceEdit;
/**
* The method level customization for an AutoRest generated method.
*/
-public final class MethodCustomization extends CodeCustomization {
- private final String packageName;
- private final String className;
- private final String methodName;
- private final String methodSignature;
-
- MethodCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName, String className,
- String methodName, String methodSignature, SymbolInformation symbol) {
- super(editor, languageClient, symbol);
- this.packageName = packageName;
- this.className = className;
- this.methodName = methodName;
- this.methodSignature = methodSignature;
- }
-
+public interface MethodCustomization extends CodeCustomization {
/**
* Gets the name of the method this customization is using.
*
* @return The name of the method.
*/
- public String getMethodName() {
- return methodName;
- }
+ String getMethodName();
/**
* Gets the name of the class containing the method.
*
* @return The name of the class containing the method.
*/
- public String getClassName() {
- return className;
- }
+ String getClassName();
/**
* Gets the Javadoc customization for this method.
*
* @return the Javadoc customization
*/
- public JavadocCustomization getJavadoc() {
- return new JavadocCustomization(editor, languageClient, fileUri, fileName,
- symbol.getLocation().getRange().getStart().getLine());
- }
+ JavadocCustomization> getJavadoc();
/**
* Rename a method in the class. This is a refactor operation. All references to this method across the client
@@ -70,12 +38,7 @@ public JavadocCustomization getJavadoc() {
* @param newName the new name for the method
* @return the current method customization for chaining
*/
- public MethodCustomization rename(String newName) {
- WorkspaceEdit edit = languageClient.renameSymbol(fileUri, symbol.getLocation().getRange().getStart(), newName);
- Utils.applyWorkspaceEdit(edit, editor, languageClient);
-
- return refreshCustomization(methodSignature.replace(methodName + "(", newName + "("));
- }
+ MethodCustomization rename(String newName);
/**
* Add an annotation to a method in the class.
@@ -83,9 +46,7 @@ public MethodCustomization rename(String newName) {
* @param annotation the annotation to add. The leading @ can be omitted.
* @return the current method customization for chaining
*/
- public MethodCustomization addAnnotation(String annotation) {
- return Utils.addAnnotation(annotation, this, () -> refreshCustomization(methodSignature));
- }
+ MethodCustomization addAnnotation(String annotation);
/**
* Remove an annotation from the method.
@@ -93,16 +54,7 @@ public MethodCustomization addAnnotation(String annotation) {
* @param annotation the annotation to remove from the method. The leading @ can be omitted.
* @return the current method customization for chaining
*/
- public MethodCustomization removeAnnotation(String annotation) {
- return Utils.removeAnnotation(this, compilationUnit -> compilationUnit.getClassByName(className)
- .get()
- .getMethodsByName(methodName)
- .stream()
- .filter(method -> Utils.declarationContainsSymbol(method.getRange().get(), symbol.getLocation().getRange()))
- .findFirst()
- .get()
- .getAnnotationByName(Utils.cleanAnnotationName(annotation)), () -> refreshCustomization(methodSignature));
- }
+ MethodCustomization removeAnnotation(String annotation);
/**
* Replace the modifier for this method.
@@ -117,12 +69,7 @@ public MethodCustomization removeAnnotation(String annotation) {
* @throws IllegalArgumentException If the {@code modifier} is less than to {@code 0} or any {@link Modifier}
* included in the bitwise OR isn't a valid method {@link Modifier}.
*/
- public MethodCustomization setModifier(int modifiers) {
- Utils.replaceModifier(symbol, editor, languageClient, "(?:.+ )?(\\w+ )" + methodName + "\\(",
- "$1" + methodName + "(", Modifier.methodModifiers(), modifiers);
-
- return refreshCustomization(methodSignature);
- }
+ MethodCustomization setModifier(int modifiers);
/**
* Replace the parameters of the method.
@@ -130,9 +77,7 @@ public MethodCustomization setModifier(int modifiers) {
* @param newParameters New method parameters.
* @return The updated MethodCustomization object.
*/
- public MethodCustomization replaceParameters(String newParameters) {
- return replaceParameters(newParameters, null);
- }
+ MethodCustomization replaceParameters(String newParameters);
/**
* Replaces the parameters of the method and adds any additional imports required by the new parameters.
@@ -142,18 +87,7 @@ public MethodCustomization replaceParameters(String newParameters) {
* are ambiguous on which to use such as {@code List} or the utility class {@code Arrays}.
* @return A new MethodCustomization representing the updated method.
*/
- public MethodCustomization replaceParameters(String newParameters, List importsToAdd) {
- String newSignature = methodName + "(" + newParameters + ")";
-
- ClassCustomization classCustomization
- = new PackageCustomization(editor, languageClient, packageName).getClass(className);
-
- ClassCustomization updatedClassCustomization
- = Utils.addImports(importsToAdd, classCustomization, classCustomization::refreshSymbol);
-
- return Utils.replaceParameters(newParameters, updatedClassCustomization.getMethod(methodSignature),
- () -> updatedClassCustomization.getMethod(newSignature));
- }
+ MethodCustomization replaceParameters(String newParameters, List importsToAdd);
/**
* Replace the body of the method.
@@ -161,9 +95,7 @@ public MethodCustomization replaceParameters(String newParameters, List
* @param newBody New method body.
* @return The updated MethodCustomization object.
*/
- public MethodCustomization replaceBody(String newBody) {
- return replaceBody(newBody, null);
- }
+ MethodCustomization replaceBody(String newBody);
/**
* Replaces the body of the method and adds any additional imports required by the new body.
@@ -173,16 +105,7 @@ public MethodCustomization replaceBody(String newBody) {
* are ambiguous on which to use such as {@code List} or the utility class {@code Arrays}.
* @return A new MethodCustomization representing the updated method.
*/
- public MethodCustomization replaceBody(String newBody, List importsToAdd) {
- ClassCustomization classCustomization
- = new PackageCustomization(editor, languageClient, packageName).getClass(className);
-
- ClassCustomization updatedClassCustomization
- = Utils.addImports(importsToAdd, classCustomization, classCustomization::refreshSymbol);
-
- return Utils.replaceBody(newBody, updatedClassCustomization.getMethod(methodSignature),
- () -> updatedClassCustomization.getMethod(methodSignature));
- }
+ MethodCustomization replaceBody(String newBody, List importsToAdd);
/**
* Change the return type of the method. The new return type will be automatically imported.
@@ -198,9 +121,7 @@ public MethodCustomization replaceBody(String newBody, List importsToAdd
* @param returnValueFormatter the return value String formatter as described above
* @return the current method customization for chaining
*/
- public MethodCustomization setReturnType(String newReturnType, String returnValueFormatter) {
- return setReturnType(newReturnType, returnValueFormatter, false);
- }
+ MethodCustomization setReturnType(String newReturnType, String returnValueFormatter);
/**
* Change the return type of the method. The new return type will be automatically imported.
@@ -219,93 +140,6 @@ public MethodCustomization setReturnType(String newReturnType, String returnValu
* parentheses, opening and closing of code blocks have to be taken care of in the {@code returnValueFormatter}.
* @return the current method customization for chaining
*/
- public MethodCustomization setReturnType(String newReturnType, String returnValueFormatter,
- boolean replaceReturnStatement) {
- List edits = new ArrayList<>();
-
- int line = symbol.getLocation().getRange().getStart().getLine();
- Position start = new Position(line, 0);
- String oldLineContent = editor.getFileLine(fileName, line);
- Position end = new Position(line, oldLineContent.length());
- String newLineContent = oldLineContent.replaceFirst("(\\w.* )?(\\w+) " + methodName + "\\(",
- "$1" + newReturnType + " " + methodName + "(");
- TextEdit signatureEdit = new TextEdit();
- signatureEdit.setNewText(newLineContent);
- signatureEdit.setRange(new Range(start, end));
- edits.add(signatureEdit);
-
- String methodIndent = Utils.getIndent(editor.getFileLine(fileName, line));
- String methodContentIndent = Utils.getIndent(editor.getFileLine(fileName, line + 1));
- String oldReturnType = oldLineContent.replaceAll(" " + methodName + "\\(.*", "")
- .replaceFirst(methodIndent + "(\\w.* )?", "")
- .trim();
- int returnLine = -1;
- while (!oldLineContent.startsWith(methodIndent + "}")) {
- if (oldLineContent.contains("return ")) {
- returnLine = line;
- }
- oldLineContent = editor.getFileLine(fileName, ++line);
- }
- if (returnLine == -1) {
- // no return statement, originally void return type
- editor.insertBlankLine(fileName, line, false);
- FileEvent blankLineEvent = new FileEvent();
- blankLineEvent.setUri(fileUri);
- blankLineEvent.setType(FileChangeType.Changed);
- languageClient.notifyWatchedFilesChanged(Collections.singletonList(blankLineEvent));
-
- TextEdit returnEdit = new TextEdit();
- returnEdit.setRange(new Range(new Position(line, 0), new Position(line, 0)));
- returnEdit.setNewText(methodContentIndent + "return " + returnValueFormatter + ";");
- edits.add(returnEdit);
- } else if (newReturnType.equals("void")) {
- // remove return statement
- TextEdit returnEdit = new TextEdit();
- returnEdit.setNewText("");
- returnEdit.setRange(new Range(new Position(returnLine, 0), new Position(line, 0)));
- edits.add(returnEdit);
- } else {
- // replace return statement
- TextEdit returnValueEdit = new TextEdit();
- String returnLineText = editor.getFileLine(fileName, returnLine);
- returnValueEdit
- .setRange(new Range(new Position(returnLine, 0), new Position(returnLine, returnLineText.length())));
- returnValueEdit.setNewText(returnLineText.replace("return ", oldReturnType + " returnValue = "));
- edits.add(returnValueEdit);
-
- editor.insertBlankLine(fileName, line, false);
- FileEvent blankLineEvent = new FileEvent();
- blankLineEvent.setUri(fileUri);
- blankLineEvent.setType(FileChangeType.Changed);
- languageClient.notifyWatchedFilesChanged(Collections.singletonList(blankLineEvent));
-
- TextEdit returnEdit = new TextEdit();
- returnEdit.setRange(new Range(new Position(line, 0), new Position(line, 0)));
-
- if (replaceReturnStatement) {
- returnEdit.setNewText(String.format(returnValueFormatter, "returnValue"));
- } else {
- returnEdit.setNewText(
- methodContentIndent + "return " + String.format(returnValueFormatter, "returnValue") + ";");
- }
-
- edits.add(returnEdit);
- }
-
- WorkspaceEdit workspaceEdit = new WorkspaceEdit();
- workspaceEdit.setChanges(Collections.singletonMap(fileUri, edits));
- Utils.applyWorkspaceEdit(workspaceEdit, editor, languageClient);
-
- Utils.organizeImportsOnRange(languageClient, editor, fileUri, new Range(start, end));
-
- String newMethodSignature
- = methodSignature.replace(oldReturnType + " " + methodName, newReturnType + " " + methodName);
-
- return refreshCustomization(newMethodSignature);
- }
-
- private MethodCustomization refreshCustomization(String methodSignature) {
- return new PackageCustomization(editor, languageClient, packageName).getClass(className)
- .getMethod(methodSignature);
- }
+ MethodCustomization setReturnType(String newReturnType, String returnValueFormatter,
+ boolean replaceReturnStatement);
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/PackageCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/PackageCustomization.java
index 38b4ab9a16e..3f600a5e5ad 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/PackageCustomization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/PackageCustomization.java
@@ -3,61 +3,24 @@
package com.microsoft.typespec.http.client.generator.core.customization;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import org.eclipse.lsp4j.SymbolInformation;
/**
* The package level customization for an AutoRest generated client library.
*/
-public final class PackageCustomization {
- private final EclipseLanguageClient languageClient;
- private final Editor editor;
- private final String packageName;
-
- PackageCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName) {
- this.editor = editor;
- this.languageClient = languageClient;
- this.packageName = packageName;
- }
-
+public interface PackageCustomization {
/**
* Gets the class level customization for a Java class in the package.
*
* @param className the simple name of the class
* @return the class level customization
*/
- public ClassCustomization getClass(String className) {
- String packagePath = packageName.replace(".", "/");
- Optional classSymbol = languageClient.findWorkspaceSymbol(className)
- .stream()
- // findWorkspace symbol finds all classes that contain the classname term
- // The filter that checks the filename only works if there are no nested classes
- // So, when customizing client classes that contain service interface, this can incorrectly return
- // the service interface instead of the client class. So, we should add another check for exact name match
- .filter(si -> si.getName().equals(className))
- .filter(si -> si.getLocation().getUri().endsWith(packagePath + "/" + className + ".java"))
- .findFirst();
-
- return Utils.returnIfPresentOrThrow(classSymbol,
- symbol -> new ClassCustomization(editor, languageClient, packageName, className, symbol),
- () -> new IllegalArgumentException(className + " does not exist in package " + packageName));
- }
+ ClassCustomization getClass(String className);
/**
* This method lists all the classes in this package.
*
* @return A list of classes that are in this package.
*/
- public List listClasses() {
- return languageClient.findWorkspaceSymbol("*")
- .stream()
- .filter(si -> si.getContainerName().equals(packageName))
- .map(classSymbol -> new ClassCustomization(editor, languageClient, packageName, classSymbol.getName(),
- classSymbol))
- .collect(Collectors.toList());
- }
+ List listClasses();
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/PropertyCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/PropertyCustomization.java
index e789ff07349..5ebe6a623d3 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/PropertyCustomization.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/PropertyCustomization.java
@@ -3,57 +3,27 @@
package com.microsoft.typespec.http.client.generator.core.customization;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.models.JavaCodeActionKind;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import org.eclipse.lsp4j.CodeAction;
-import org.eclipse.lsp4j.SymbolInformation;
-import org.eclipse.lsp4j.SymbolKind;
-import org.eclipse.lsp4j.WorkspaceEdit;
/**
* Customization for an AutoRest generated instance property.
*
* For constant property customizations use {@link ConstantCustomization}.
*/
-public final class PropertyCustomization extends CodeCustomization {
- private static final Pattern METHOD_PARAMS_CAPTURE = Pattern.compile("\\(.*\\)");
-
- private final String packageName;
- private final String className;
- private final String propertyName;
-
- PropertyCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName, String className,
- SymbolInformation symbol, String propertyName) {
- super(editor, languageClient, symbol);
- this.packageName = packageName;
- this.className = className;
- this.propertyName = propertyName;
- }
-
+public interface PropertyCustomization extends CodeCustomization {
/**
* Gets the name of the class that contains this property.
*
* @return The name of the class that contains this property.
*/
- public String getClassName() {
- return className;
- }
+ String getClassName();
/**
* Gets the name of this property.
*
* @return The name of this property.
*/
- public String getPropertyName() {
- return propertyName;
- }
+ String getPropertyName();
/**
* Rename a property in the class. This is a refactor operation. All references of the property will be renamed and
@@ -62,29 +32,7 @@ public String getPropertyName() {
* @param newName the new name for the property
* @return the current class customization for chaining
*/
- public PropertyCustomization rename(String newName) {
- List symbols = languageClient.listDocumentSymbols(fileUri)
- .stream()
- .filter(si -> si.getName().toLowerCase().contains(propertyName.toLowerCase()))
- .collect(Collectors.toList());
- String propertyPascalName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
- String newPascalName = newName.substring(0, 1).toUpperCase() + newName.substring(1);
-
- List edits = new ArrayList<>();
- for (SymbolInformation symbol : symbols) {
- if (symbol.getKind() == SymbolKind.Field) {
- edits.add(languageClient.renameSymbol(fileUri, symbol.getLocation().getRange().getStart(), newName));
- } else if (symbol.getKind() == SymbolKind.Method) {
- String methodName
- = symbol.getName().replace(propertyPascalName, newPascalName).replace(propertyName, newName);
- methodName = METHOD_PARAMS_CAPTURE.matcher(methodName).replaceFirst("");
- edits.add(languageClient.renameSymbol(fileUri, symbol.getLocation().getRange().getStart(), methodName));
- }
- }
-
- Utils.applyWorkspaceEdits(edits, editor, languageClient);
- return refreshCustomization(newName);
- }
+ PropertyCustomization rename(String newName);
/**
* Add an annotation to a property in the class.
@@ -92,9 +40,7 @@ public PropertyCustomization rename(String newName) {
* @param annotation the annotation to add. The leading @ can be omitted.
* @return the current property customization for chaining
*/
- public PropertyCustomization addAnnotation(String annotation) {
- return Utils.addAnnotation(annotation, this, () -> refreshCustomization(propertyName));
- }
+ PropertyCustomization addAnnotation(String annotation);
/**
* Remove an annotation from the property.
@@ -102,15 +48,7 @@ public PropertyCustomization addAnnotation(String annotation) {
* @param annotation the annotation to remove from the property. The leading @ can be omitted.
* @return the current property customization for chaining
*/
- public PropertyCustomization removeAnnotation(String annotation) {
- return Utils.removeAnnotation(this,
- compilationUnit -> compilationUnit.getClassByName(className)
- .get()
- .getFieldByName(propertyName)
- .get()
- .getAnnotationByName(Utils.cleanAnnotationName(annotation)),
- () -> refreshCustomization(propertyName));
- }
+ PropertyCustomization removeAnnotation(String annotation);
/**
* Generates a getter and a setter method(s) for a property in the class. This is a refactor operation. If a getter
@@ -118,24 +56,7 @@ public PropertyCustomization removeAnnotation(String annotation) {
*
* @return the current class customization for chaining
*/
- public PropertyCustomization generateGetterAndSetter() {
- Optional generateAccessors = languageClient
- .listCodeActions(fileUri, symbol.getLocation().getRange(),
- JavaCodeActionKind.SOURCE_GENERATE_ACCESSORS.toString())
- .stream()
- .filter(ca -> ca.getKind().equals(JavaCodeActionKind.SOURCE_GENERATE_ACCESSORS.toString()))
- .findFirst();
- if (generateAccessors.isPresent()) {
- Utils.applyWorkspaceEdit(generateAccessors.get().getEdit(), editor, languageClient);
-
- String setterMethod = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
- new PackageCustomization(editor, languageClient, packageName).getClass(className)
- .getMethod(setterMethod)
- .setReturnType(className, "this");
- }
-
- return this;
- }
+ PropertyCustomization generateGetterAndSetter();
/**
* Replace the modifier for this property.
@@ -150,20 +71,5 @@ public PropertyCustomization generateGetterAndSetter() {
* @throws IllegalArgumentException If the {@code modifier} is less than {@code 0} or any {@link Modifier} included
* in the bitwise OR isn't a valid property {@link Modifier}.
*/
- public PropertyCustomization setModifier(int modifiers) {
- String target = " *(?:(?:public|protected|private|static|final|transient|volatile) ?)*(.* )";
- languageClient.listDocumentSymbols(symbol.getLocation().getUri())
- .stream()
- .filter(si -> si.getKind() == SymbolKind.Field && si.getName().equals(propertyName))
- .findFirst()
- .ifPresent(symbolInformation -> Utils.replaceModifier(symbolInformation, editor, languageClient,
- target + propertyName, "$1" + propertyName, Modifier.fieldModifiers(), modifiers));
-
- return refreshCustomization(propertyName);
- }
-
- private PropertyCustomization refreshCustomization(String propertyName) {
- return new PackageCustomization(editor, languageClient, packageName).getClass(className)
- .getProperty(propertyName);
- }
+ PropertyCustomization setModifier(int modifiers);
}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/Utils.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/Utils.java
index 415a05fbacd..9ee9bdcd2c4 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/Utils.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/Utils.java
@@ -3,35 +3,15 @@
package com.microsoft.typespec.http.client.generator.core.customization.implementation;
-import com.github.javaparser.StaticJavaParser;
-import com.github.javaparser.ast.CompilationUnit;
-import com.github.javaparser.ast.expr.AnnotationExpr;
-import com.microsoft.typespec.http.client.generator.core.customization.ClassCustomization;
-import com.microsoft.typespec.http.client.generator.core.customization.CodeCustomization;
import com.microsoft.typespec.http.client.generator.core.customization.Editor;
-import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
import java.io.File;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import org.eclipse.lsp4j.CodeActionKind;
-import org.eclipse.lsp4j.FileChangeType;
-import org.eclipse.lsp4j.FileEvent;
-import org.eclipse.lsp4j.Position;
-import org.eclipse.lsp4j.Range;
-import org.eclipse.lsp4j.SymbolInformation;
-import org.eclipse.lsp4j.TextEdit;
-import org.eclipse.lsp4j.WorkspaceEdit;
public class Utils {
/**
@@ -40,97 +20,11 @@ public class Utils {
*/
public static final Pattern INDENT_DETERMINATION_PATTERN = Pattern.compile("^(\\s*).*$");
- /**
- * This pattern matches a Java package declaration.
- */
- private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s[\\w\\.]+;");
-
/**
* This pattern matches anything then the space.
*/
public static final Pattern ANYTHING_THEN_SPACE_PATTERN = Pattern.compile(".* ");
- /*
- * This pattern determines if a line is a beginning of constructor or method. The following is an explanation of
- * the pattern:
- *
- * 1. Capture all leading space characters.
- * 2. Capture all modifiers for the constructor or method.
- * 3. If a method, capture the return type.
- * 4. Capture the name of the constructor or method.
- *
- * The following are the groups return:
- *
- * 1. The entire matching declaration from the beginning of the string used to determine the beginning offset of the
- * parameters in the constructor or method.
- * 2. Any modifiers for the constructor or method. This may be empty/null.
- * 3. If a method, the return type. If a constructor, empty/null.
- * 4. The name of the constructor or method.
- */
- private static final Pattern BEGINNING_OF_PARAMETERS_PATTERN
- = Pattern.compile("^(\\s*(?:([\\w\\s]*?)\\s)?(?:([a-zA-Z$_][\\w]*?)\\s+)?([a-zA-Z$_][\\w]*?)\\s*)\\(.*$");
-
- private static final Pattern ENDING_OF_PARAMETERS_PATTERN = Pattern.compile("^(.*)\\)\\s*\\{.*$");
-
- public static void applyWorkspaceEdit(WorkspaceEdit workspaceEdit, Editor editor,
- EclipseLanguageClient languageClient) {
- Map changes = new HashMap<>();
- applyWorkspaceEditInternal(workspaceEdit.getChanges(), changes, editor);
- languageClient.notifyWatchedFilesChanged(new ArrayList<>(changes.values()));
- }
-
- public static void applyWorkspaceEdits(List workspaceEdits, Editor editor,
- EclipseLanguageClient languageClient) {
- if (workspaceEdits == null || workspaceEdits.isEmpty()) {
- return;
- }
-
- Map changes = new HashMap<>();
- for (WorkspaceEdit workspaceEdit : workspaceEdits) {
- applyWorkspaceEditInternal(workspaceEdit.getChanges(), changes, editor);
- }
-
- languageClient.notifyWatchedFilesChanged(new ArrayList<>(changes.values()));
- }
-
- private static void applyWorkspaceEditInternal(Map> edits, Map changes,
- Editor editor) {
- if (edits == null || edits.isEmpty()) {
- return;
- }
-
- for (Map.Entry> edit : edits.entrySet()) {
- int i = edit.getKey().indexOf("src/main/java/");
- String fileName = edit.getKey().substring(i);
- if (editor.getContents().containsKey(fileName)) {
- for (TextEdit textEdit : edit.getValue()) {
- editor.replace(fileName, textEdit.getRange().getStart(), textEdit.getRange().getEnd(),
- textEdit.getNewText());
- }
- changes.putIfAbsent(fileName, new FileEvent(edit.getKey(), FileChangeType.Changed));
- }
- }
- }
-
- public static void applyTextEdits(String fileUri, List textEdits, Editor editor,
- EclipseLanguageClient languageClient) {
- List changes = new ArrayList<>();
- int i = fileUri.indexOf("src/main/java/");
- String fileName = fileUri.substring(i);
- if (editor.getContents().containsKey(fileName)) {
- for (int j = textEdits.size() - 1; j >= 0; j--) {
- TextEdit textEdit = textEdits.get(j);
- editor.replace(fileName, textEdit.getRange().getStart(), textEdit.getRange().getEnd(),
- textEdit.getNewText());
- }
- FileEvent fileEvent = new FileEvent();
- fileEvent.setUri(fileUri);
- fileEvent.setType(FileChangeType.Changed);
- changes.add(fileEvent);
- }
- languageClient.notifyWatchedFilesChanged(changes);
- }
-
public static void deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
@@ -153,65 +47,9 @@ public static boolean isNullOrEmpty(Iterable iterable) {
return (iterable == null || !iterable.iterator().hasNext());
}
- static void validateModifiers(int validTypeModifiers, int newModifiers) {
- // 0 indicates no modifiers.
- if (newModifiers == 0) {
- return;
- }
-
- if (newModifiers < 0) {
- throw new IllegalArgumentException("Modifiers aren't allowed to be less than or equal to 0.");
- }
-
- if (validTypeModifiers != (validTypeModifiers | newModifiers)) {
- throw new IllegalArgumentException("Modifiers contain illegal modifiers for the type.");
- }
- }
-
- /**
- * Replaces the modifier for a given symbol.
- *
- * @param symbol The symbol having its modifier replaced.
- * @param editor The editor containing information about the symbol.
- * @param languageClient The language client handling replacement of the modifiers.
- * @param replaceTarget A string regex that determines how the modifiers are replaced.
- * @param modifierReplaceBase A string that determines the base modifier replacement.
- * @param validaTypeModifiers The modifier bit flag used to validate the new modifiers.
- * @param newModifiers The new modifiers for the symbol.
- */
- public static void replaceModifier(SymbolInformation symbol, Editor editor, EclipseLanguageClient languageClient,
- String replaceTarget, String modifierReplaceBase, int validaTypeModifiers, int newModifiers) {
- validateModifiers(validaTypeModifiers, newModifiers);
-
- String fileUri = symbol.getLocation().getUri();
- int i = fileUri.indexOf("src/main/java/");
- String fileName = fileUri.substring(i);
-
- int line = symbol.getLocation().getRange().getStart().getLine();
- Position start = new Position(line, 0);
- String oldLineContent = editor.getFileLine(fileName, line);
- Position end = new Position(line, oldLineContent.length());
-
- String newModifiersString = Modifier.toString(newModifiers);
- String newLineContent = (isNullOrEmpty(newModifiersString))
- ? oldLineContent.replaceFirst(replaceTarget, modifierReplaceBase)
- : oldLineContent.replaceFirst(replaceTarget, newModifiersString + " " + modifierReplaceBase);
-
- TextEdit textEdit = new TextEdit();
- textEdit.setNewText(newLineContent);
- textEdit.setRange(new Range(start, end));
- WorkspaceEdit workspaceEdit = new WorkspaceEdit();
- workspaceEdit.setChanges(Collections.singletonMap(fileUri, Collections.singletonList(textEdit)));
- Utils.applyWorkspaceEdit(workspaceEdit, editor, languageClient);
- }
-
public static S returnIfPresentOrThrow(Optional optional, Function returnFormatter,
Supplier orThrow) {
- if (optional.isPresent()) {
- return returnFormatter.apply(optional.get());
- }
-
- throw orThrow.get();
+ return optional.map(returnFormatter).orElseThrow(orThrow);
}
public static void writeLine(StringBuilder stringBuilder, String text) {
@@ -230,86 +68,24 @@ public static void writeLine(StringBuilder stringBuilder, String text) {
*/
public static int walkDownFileUntilLineMatches(Editor editor, String fileName, int startLine,
Predicate linePredicate) {
- return walkFileUntilLineMatches(editor, fileName, startLine, linePredicate, true);
- }
-
- /**
- * Walks up the lines of a file until the line matches a predicate.
- *
- * @param editor The editor containing the file's information.
- * @param fileName The name of the file.
- * @param startLine The line to start walking.
- * @param linePredicate The predicate that determines when a matching line is found.
- * @return The first line that matches the predicate. If no line in the file matches the predicate {@code -1} is
- * returned.
- */
- public static int walkUpFileUntilLineMatches(Editor editor, String fileName, int startLine,
- Predicate linePredicate) {
- return walkFileUntilLineMatches(editor, fileName, startLine, linePredicate, false);
+ return walkFileUntilLineMatches(editor, fileName, startLine, linePredicate);
}
private static int walkFileUntilLineMatches(Editor editor, String fileName, int startLine,
- Predicate linePredicate, boolean isWalkingDown) {
+ Predicate linePredicate) {
int matchingLine = -1;
List fileLines = editor.getFileLines(fileName);
- if (isWalkingDown) {
- for (int line = startLine; line < fileLines.size(); line++) {
- if (linePredicate.test(fileLines.get(line))) {
- matchingLine = line;
- break;
- }
- }
- } else {
- for (int line = startLine; line >= 0; line--) {
- if (linePredicate.test(fileLines.get(line))) {
- matchingLine = line;
- break;
- }
+ for (int line = startLine; line < fileLines.size(); line++) {
+ if (linePredicate.test(fileLines.get(line))) {
+ matchingLine = line;
+ break;
}
}
return matchingLine;
}
- /**
- * Utility method to add an annotation to a code block.
- *
- * @param annotation The annotation to add.
- * @param customization The customization having an annotation added.
- * @param refreshedCustomizationSupplier A supplier that returns a refreshed customization after the annotation is
- * added.
- * @param The type of the customization.
- * @return A refreshed customization after the annotation was added.
- */
- public static T addAnnotation(String annotation, CodeCustomization customization,
- Supplier refreshedCustomizationSupplier) {
- SymbolInformation symbol = customization.getSymbol();
- Editor editor = customization.getEditor();
- String fileName = customization.getFileName();
- String fileUri = customization.getFileUri();
- EclipseLanguageClient languageClient = customization.getLanguageClient();
-
- if (!annotation.startsWith("@")) {
- annotation = "@" + annotation;
- }
-
- if (editor.getContents().containsKey(fileName)) {
- int line = symbol.getLocation().getRange().getStart().getLine();
- Position position = editor.insertBlankLine(fileName, line, true);
- editor.replace(fileName, position, position, annotation);
-
- FileEvent fileEvent = new FileEvent();
- fileEvent.setUri(fileUri);
- fileEvent.setType(FileChangeType.Changed);
- languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
-
- organizeImportsOnRange(languageClient, editor, fileUri, symbol.getLocation().getRange());
- }
-
- return refreshedCustomizationSupplier.get();
- }
-
/**
* Removes the leading {@literal @} in an annotation name, if it exists.
*
@@ -320,197 +96,11 @@ public static String cleanAnnotationName(String annotationName) {
return annotationName.startsWith("@") ? annotationName.substring(1) : annotationName;
}
- /**
- * Utility method to remove an annotation from a code block.
- *
- * @param codeCustomization The customization having an annotation removed.
- * @param annotationRetriever Function that retrieves the potential annotation.
- * @param refreshedCustomizationSupplier Supplier that returns a refreshed customization after the annotation is
- * removed.
- * @param The type of the customization.
- * @return A refreshed customization if the annotation was removed, otherwise the customization as-is.
- */
- public static T removeAnnotation(T codeCustomization,
- Function> annotationRetriever,
- Supplier refreshedCustomizationSupplier) {
- Editor editor = codeCustomization.getEditor();
- String fileName = codeCustomization.getFileName();
-
- CompilationUnit compilationUnit = StaticJavaParser.parse(editor.getFileContent(fileName));
- Optional potentialAnnotation = annotationRetriever.apply(compilationUnit);
-
- if (potentialAnnotation.isPresent()) {
- potentialAnnotation.get().remove();
- editor.replaceFile(fileName, compilationUnit.toString());
- Utils.sendFilesChangeNotification(codeCustomization.getLanguageClient(), codeCustomization.getFileUri());
- return refreshedCustomizationSupplier.get();
- } else {
- return codeCustomization;
- }
- }
-
- /**
- * Notifies watchers of a file that it has changed.
- *
- * @param languageClient The {@link EclipseLanguageClient} sending the file changed notification.
- * @param fileUri The URI of the file that was changed.
- */
- public static void sendFilesChangeNotification(EclipseLanguageClient languageClient, String fileUri) {
- FileEvent fileEvent = new FileEvent();
- fileEvent.setUri(fileUri);
- fileEvent.setType(FileChangeType.Changed);
- languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
- }
-
- public static boolean declarationContainsSymbol(com.github.javaparser.Range declarationRange, Range symbolRange) {
- return declarationRange.begin.line <= symbolRange.getStart().getLine()
- && declarationRange.end.line >= symbolRange.getStart().getLine();
- }
-
- /**
- * Utility method to replace a body of a code block.
- *
- * @param newBody The new body.
- * @param customization The customization having its body replaced.
- * @param refreshedCustomizationSupplier A supplier that returns a refreshed customization after the body is
- * replaced.
- * @param The type of the customization.
- * @return A refreshed customization after the body was replaced.
- */
- public static T replaceBody(String newBody, CodeCustomization customization,
- Supplier refreshedCustomizationSupplier) {
- SymbolInformation symbol = customization.getSymbol();
- Editor editor = customization.getEditor();
- String fileName = customization.getFileName();
-
- int line = symbol.getLocation().getRange().getStart().getLine();
- String methodBlockIndent = getIndent(editor.getFileLine(fileName, line));
-
- // Loop until the line containing the body start is found.
- Pattern startPattern = Pattern.compile(".*\\{\\s*");
- int startLine = walkDownFileUntilLineMatches(editor, fileName, line,
- lineContent -> startPattern.matcher(lineContent).matches()) + 1; // Plus one since the start is after the
- // opening '{'
-
- // Then determine the base indentation level for the body.
- String methodContentIndent = getIndent(editor.getFileLine(fileName, startLine));
- Position oldBodyStart = new Position(startLine, methodContentIndent.length());
-
- // Then continue iterating over lines until the body close line is found.
- Pattern closePattern = Pattern.compile(methodBlockIndent + "}\\s*");
- int lastLine = walkDownFileUntilLineMatches(editor, fileName, startLine,
- lineContent -> closePattern.matcher(lineContent).matches()) - 1; // Minus one since the end is before the
- // closing '}'
- Position oldBodyEnd = new Position(lastLine, editor.getFileLine(fileName, lastLine).length());
-
- editor.replaceWithIndentedContent(fileName, oldBodyStart, oldBodyEnd, newBody, methodContentIndent.length());
- FileEvent fileEvent = new FileEvent();
- fileEvent.setUri(customization.getFileUri());
- fileEvent.setType(FileChangeType.Changed);
- customization.getLanguageClient().notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
-
- // Return the refreshed customization.
- return refreshedCustomizationSupplier.get();
- }
-
- public static T replaceParameters(String newParameters,
- CodeCustomization customization, Supplier refreshCustomizationSupplier) {
- SymbolInformation symbol = customization.getSymbol();
- Editor editor = customization.getEditor();
- String fileName = customization.getFileName();
- String fileUri = customization.getFileUri();
- EclipseLanguageClient languageClient = customization.getLanguageClient();
-
- // Beginning line of the symbol.
- int line = symbol.getLocation().getRange().getStart().getLine();
-
- // First find the starting location of the parameters.
- // The beginning of the parameters may not be on the same line as the start of the signature.
- Matcher matcher = BEGINNING_OF_PARAMETERS_PATTERN.matcher(editor.getFileLine(fileName, line));
- while (!matcher.matches()) {
- matcher = BEGINNING_OF_PARAMETERS_PATTERN.matcher(editor.getFileLine(fileName, ++line));
- }
-
- // Now that the line where the parameters begin is found create its position.
- // Starting character is inclusive of the character offset, so add one as ')' isn't included in the capture.
- Position parametersStart = new Position(line, matcher.group(1).length() + 1);
-
- // Then find where the parameters end.
- // The ending of the parameters may not be on the same line as the start of the parameters.
- matcher = ENDING_OF_PARAMETERS_PATTERN.matcher(editor.getFileLine(fileName, line));
- while (!matcher.matches()) {
- matcher = ENDING_OF_PARAMETERS_PATTERN.matcher(editor.getFileLine(fileName, ++line));
- }
-
- // Now that the line where the parameters end is found gets create its position.
- // Ending character is exclusive of the character offset.
- Position parametersEnd = new Position(line, matcher.group(1).length());
-
- editor.replace(fileName, parametersStart, parametersEnd, newParameters);
-
- FileEvent fileEvent = new FileEvent();
- fileEvent.setUri(fileUri);
- fileEvent.setType(FileChangeType.Changed);
- languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
-
- return refreshCustomizationSupplier.get();
- }
-
public static String getIndent(String content) {
Matcher matcher = INDENT_DETERMINATION_PATTERN.matcher(content);
return matcher.matches() ? matcher.group(1) : "";
}
- /**
- * Adds imports to the customization.
- *
- * @param importsToAdd Imports to add.
- * @param customization Code customization to add imports.
- * @param refreshCustomizationSupplier A supplier that returns a refreshed customization after the imports are
- * added.
- * @param Type of the customization.
- * @return A refreshed customization.
- */
- public static T addImports(List importsToAdd,
- ClassCustomization customization, Supplier refreshCustomizationSupplier) {
- EclipseLanguageClient languageClient = customization.getLanguageClient();
- Editor editor = customization.getEditor();
- String fileUri = customization.getFileUri();
- String fileName = customization.getFileName();
-
- // Only add imports if they exist.
- if (!isNullOrEmpty(importsToAdd)) {
- // Always place imports after the package.
- // The language server will format the imports once added, so location doesn't matter.
- int importLine = Utils.walkDownFileUntilLineMatches(editor, fileName, 0,
- line -> PACKAGE_PATTERN.matcher(line).matches()) + 1;
-
- Position importPosition = new Position(importLine, 0);
- String imports = importsToAdd.stream()
- .map(importToAdd -> "import " + importToAdd + ";")
- .collect(Collectors.joining("\n"));
-
- editor.insertBlankLine(fileName, importLine, false);
- editor.replace(fileName, importPosition, importPosition, imports);
- }
-
- FileEvent fileEvent = new FileEvent();
- fileEvent.setUri(fileUri);
- fileEvent.setType(FileChangeType.Changed);
- languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
-
- return refreshCustomizationSupplier.get();
- }
-
- public static void organizeImportsOnRange(EclipseLanguageClient languageClient, Editor editor, String fileUri,
- Range range) {
- languageClient.listCodeActions(fileUri, range, CodeActionKind.SourceOrganizeImports)
- .stream()
- .filter(ca -> ca.getKind().equals(CodeActionKind.SourceOrganizeImports))
- .findFirst()
- .ifPresent(action -> Utils.applyWorkspaceEdit(action.getEdit(), editor, languageClient));
- }
-
public static boolean isWindows() {
String osName = System.getProperty("os.name");
return osName != null && osName.startsWith("Windows");
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseClassCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseClassCustomization.java
new file mode 100644
index 00000000000..6c63c3ec140
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseClassCustomization.java
@@ -0,0 +1,517 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.github.javaparser.StaticJavaParser;
+import com.github.javaparser.ast.CompilationUnit;
+import com.microsoft.typespec.http.client.generator.core.customization.ClassCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.eclipse.lsp4j.FileChangeType;
+import org.eclipse.lsp4j.FileEvent;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.SymbolKind;
+import org.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+
+/**
+ * The class level customization for an AutoRest generated class.
+ */
+public final class EclipseClassCustomization extends EclipseCodeCustomization implements ClassCustomization {
+ private static final int INDENT_LENGTH = 4;
+
+ /*
+ * This pattern attempts to find the first line of a method string that doesn't have a first non-space character of
+ * '*' or '/'. From there it captures all word and space characters before and inside '( )' ignoring any trailing
+ * spaces and an opening '{'.
+ */
+ private static final Pattern METHOD_SIGNATURE_PATTERN
+ = Pattern.compile("^\\s*([^/*][\\w\\s]+\\([\\w\\s<>,.]*\\))\\s*\\{?$", Pattern.MULTILINE);
+
+ /*
+ * This pattern attempts to find the first line of a constructor string that doesn't have a first non-space
+ * character of '*' or '/', effectively the first non-Javadoc line. From there it captures all word and space
+ * characters before and inside '( )' ignoring any trailing spaces and an opening '{'.
+ */
+ private static final Pattern CONSTRUCTOR_SIGNATURE_PATTERN
+ = Pattern.compile("^\\s*([^/*][\\w\\s]+\\([\\w\\s<>,.]*\\))\\s*\\{?$", Pattern.MULTILINE);
+
+ private static final Pattern BLOCK_OPEN = Pattern.compile("\\) *\\{");
+ private static final Pattern PUBLIC_MODIFIER = Pattern.compile(" *public ");
+ private static final Pattern PRIVATE_MODIFIER = Pattern.compile(" *private ");
+ private static final Pattern MEMBER_PARAMS = Pattern.compile("\\(.*\\)");
+
+ private final String packageName;
+ private final String className;
+
+ EclipseClassCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName, String className,
+ SymbolInformation classSymbol) {
+ super(editor, languageClient, classSymbol);
+
+ this.packageName = packageName;
+ this.className = className;
+ }
+
+ @Override
+ public String getClassName() {
+ return className;
+ }
+
+ @Override
+ public EclipseClassCustomization addImports(String... imports) {
+ if (imports != null) {
+ return EclipseUtils.addImports(Arrays.asList(imports), this, this::refreshSymbol);
+ }
+
+ return this;
+ }
+
+ @Override
+ public EclipseClassCustomization addStaticBlock(String staticCodeBlock) {
+ return addStaticBlock(staticCodeBlock, null);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseClassCustomization addStaticBlock(String staticCodeBlock, List importsToAdd) {
+ if (Utils.isNullOrEmpty(staticCodeBlock)) {
+ return this;
+ }
+
+ // the class declaration line
+ int lastSymbolLine = symbol.getLocation().getRange().getStart().getLine();
+
+ // Find the last field symbol.
+ Optional lastSymbol = languageClient.listDocumentSymbols(fileUri)
+ .stream()
+ .filter(symbol -> symbol.getKind() == SymbolKind.Field)
+ .reduce((first, second) -> second);
+
+ int indentAmount = INDENT_LENGTH;
+ if (lastSymbol.isPresent()) {
+ // the line number of the last field declaration
+ lastSymbolLine = lastSymbol.get().getLocation().getRange().getStart().getLine();
+ indentAmount = Utils.getIndent(editor.getFileLine(fileName, lastSymbolLine)).length();
+ }
+// System.out.println("indent amount " + indentAmount);
+
+ // start the static block from the next line of the last field or the line after class declaration
+ int staticBlockStartLine = lastSymbolLine + 1;
+ editor.insertBlankLine(fileName, staticBlockStartLine, false);
+ Position staticBlockPosition = editor.insertBlankLineWithIndent(fileName, staticBlockStartLine, indentAmount);
+ if (!staticCodeBlock.trim().startsWith("static")) {
+ staticCodeBlock = "static { " + System.lineSeparator() + staticCodeBlock + System.lineSeparator() + "}";
+ }
+
+ editor.replaceWithIndentedContent(fileName, staticBlockPosition, staticBlockPosition, staticCodeBlock,
+ staticBlockPosition.getCharacter());
+ if (importsToAdd != null) {
+ return EclipseUtils.addImports(importsToAdd, this, this::refreshSymbol);
+ }
+ return this;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseMethodCustomization getMethod(String methodNameOrSignature) {
+ String methodName;
+ String methodSignature = null;
+ if (methodNameOrSignature.contains("(")) {
+ // method signature
+ methodSignature = BLOCK_OPEN.matcher(methodNameOrSignature).replaceFirst("");
+ methodSignature = PUBLIC_MODIFIER.matcher(methodSignature).replaceFirst("");
+ methodSignature = PRIVATE_MODIFIER.matcher(methodSignature).replaceFirst("");
+
+ String returnTypeAndMethodName = methodNameOrSignature.split("\\(")[0];
+ if (returnTypeAndMethodName.contains(" ")) {
+ methodName = Utils.ANYTHING_THEN_SPACE_PATTERN.matcher(returnTypeAndMethodName).replaceAll("");
+ } else {
+ methodName = returnTypeAndMethodName;
+ }
+ } else {
+ methodName = methodNameOrSignature;
+ }
+ Optional methodSymbol = languageClient.listDocumentSymbols(fileUri)
+ .stream()
+ .filter(si -> si.getKind() == SymbolKind.Method
+ && MEMBER_PARAMS.matcher(si.getName()).replaceFirst("").equals(methodName))
+ .filter(si -> editor.getFileLine(fileName, si.getLocation().getRange().getStart().getLine())
+ .contains(methodNameOrSignature))
+ .findFirst();
+ if (methodSymbol.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Method " + methodNameOrSignature + " does not exist in class " + className);
+ }
+ if (methodSignature == null) {
+ methodSignature
+ = editor.getFileLine(fileName, methodSymbol.get().getLocation().getRange().getStart().getLine());
+ methodSignature = BLOCK_OPEN.matcher(methodSignature).replaceFirst("");
+ methodSignature = PUBLIC_MODIFIER.matcher(methodSignature).replaceFirst("");
+ methodSignature = PRIVATE_MODIFIER.matcher(methodSignature).replaceFirst("");
+ }
+ return new EclipseMethodCustomization(editor, languageClient, packageName, className, methodName,
+ methodSignature, methodSymbol.get());
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseConstructorCustomization getConstructor(String constructorNameOrSignature) {
+ String constructorName;
+ String constructorSignature = null;
+ if (constructorNameOrSignature.contains("(")) {
+ // method signature
+ constructorSignature = BLOCK_OPEN.matcher(constructorNameOrSignature).replaceFirst("");
+ constructorSignature = PUBLIC_MODIFIER.matcher(constructorSignature).replaceFirst("");
+ constructorSignature = PRIVATE_MODIFIER.matcher(constructorSignature).replaceFirst("");
+ String returnTypeAndMethodName = constructorNameOrSignature.split("\\(")[0];
+ if (returnTypeAndMethodName.contains(" ")) {
+ constructorName = Utils.ANYTHING_THEN_SPACE_PATTERN.matcher(returnTypeAndMethodName).replaceAll("");
+ } else {
+ constructorName = returnTypeAndMethodName;
+ }
+ } else {
+ constructorName = constructorNameOrSignature;
+ }
+
+ List constructorSymbol = languageClient.listDocumentSymbols(fileUri)
+ .stream()
+ .filter(si -> si.getKind() == SymbolKind.Constructor
+ && MEMBER_PARAMS.matcher(si.getName()).replaceFirst("").equals(constructorName))
+ .filter(si -> editor.getFileLine(fileName, si.getLocation().getRange().getStart().getLine())
+ .contains(constructorNameOrSignature))
+ .collect(Collectors.toList());
+
+ if (constructorSymbol.size() > 1) {
+ throw new IllegalStateException("Multiple instances of " + constructorNameOrSignature + " exist in the "
+ + "class. Use a more specific constructor signature.");
+ }
+
+ if (constructorSymbol.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Constructor " + constructorNameOrSignature + " does not exist in class " + className);
+ }
+
+ if (constructorSignature == null) {
+ constructorSignature
+ = editor.getFileLine(fileName, constructorSymbol.get(0).getLocation().getRange().getStart().getLine());
+ constructorSignature = BLOCK_OPEN.matcher(constructorSignature).replaceFirst("");
+ constructorSignature = PUBLIC_MODIFIER.matcher(constructorSignature).replaceFirst("");
+ constructorSignature = PRIVATE_MODIFIER.matcher(constructorSignature).replaceFirst("");
+ }
+ return new EclipseConstructorCustomization(editor, languageClient, packageName, className, constructorSignature,
+ constructorSymbol.get(0));
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipsePropertyCustomization getProperty(String propertyName) {
+ Optional propertySymbol = languageClient.listDocumentSymbols(fileUri)
+ .stream()
+ .filter(si -> si.getKind() == SymbolKind.Field && si.getName().equals(propertyName))
+ .findFirst();
+
+ if (propertySymbol.isEmpty()) {
+ throw new IllegalArgumentException("Property " + propertyName + " does not exist in class " + className);
+ }
+
+ return new EclipsePropertyCustomization(editor, languageClient, packageName, className, propertySymbol.get(),
+ propertyName);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseConstantCustomization getConstant(String constantName) {
+ Optional propertySymbol = languageClient.listDocumentSymbols(fileUri)
+ .stream()
+ .filter(si -> si.getKind() == SymbolKind.Constant && si.getName().equals(constantName))
+ .findFirst();
+
+ if (propertySymbol.isEmpty()) {
+ throw new IllegalArgumentException("Constant " + constantName + " does not exist in class " + className);
+ }
+
+ return new EclipseConstantCustomization(editor, languageClient, packageName, className, propertySymbol.get(),
+ constantName);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseJavadocCustomization getJavadoc() {
+ return new EclipseJavadocCustomization(editor, languageClient, fileUri, fileName,
+ symbol.getLocation().getRange().getStart().getLine());
+ }
+
+ @Override
+ public EclipseConstructorCustomization addConstructor(String constructor) {
+ return addConstructor(constructor, null);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseConstructorCustomization addConstructor(String constructor, List importsToAdd) {
+ // Get the signature of the constructor.
+ Matcher constructorSignatureMatcher = CONSTRUCTOR_SIGNATURE_PATTERN.matcher(constructor);
+ String constructorSignature = null;
+ if (constructorSignatureMatcher.find()) {
+ constructorSignature = constructorSignatureMatcher.group(1);
+ }
+
+ // Find all constructor and field symbols.
+ List constructorLocationFinder = languageClient.listDocumentSymbols(fileUri)
+ .stream()
+ .filter(symbol -> symbol.getKind() == SymbolKind.Field || symbol.getKind() == SymbolKind.Constructor)
+ .collect(Collectors.toList());
+
+ // If no constructors or fields exist in the class place the constructor after the class declaration line.
+ // Otherwise place the constructor after the last constructor or field.
+ int constructorStartLine;
+ if (Utils.isNullOrEmpty(constructorLocationFinder)) {
+ constructorStartLine = symbol.getLocation().getRange().getStart().getLine();
+ } else {
+ SymbolInformation symbol = constructorLocationFinder.get(constructorLocationFinder.size() - 1);
+
+ // If the last symbol before the new constructor is a field only a new line needs to be inserted.
+ // Otherwise if the last symbol is a constructor its closing '}' needs to be found and then a new line
+ // needs to be inserted.
+ if (symbol.getKind() == SymbolKind.Field) {
+ constructorStartLine = symbol.getLocation().getRange().getStart().getLine();
+ } else {
+ constructorStartLine = symbol.getLocation().getRange().getStart().getLine();
+
+ List fileLines = editor.getFileLines(fileName);
+ String currentLine = fileLines.get(constructorStartLine);
+ String constructorIdent = Utils.getIndent(currentLine);
+ while (!currentLine.endsWith("}") || !currentLine.equals(constructorIdent + "}")) {
+ currentLine = fileLines.get(++constructorStartLine);
+ }
+ }
+ }
+
+ int indentAmount = Utils.getIndent(editor.getFileLine(fileName, constructorStartLine)).length();
+
+ editor.insertBlankLine(fileName, ++constructorStartLine, false);
+ Position constructorPosition = editor.insertBlankLineWithIndent(fileName, ++constructorStartLine, indentAmount);
+
+ editor.replaceWithIndentedContent(fileName, constructorPosition, constructorPosition, constructor,
+ constructorPosition.getCharacter());
+
+ final String ctorSignature = (constructorSignature == null)
+ ? editor.getFileLine(fileName, constructorStartLine)
+ : constructorSignature;
+
+ return EclipseUtils.addImports(importsToAdd, this, () -> getConstructor(ctorSignature));
+ }
+
+ @Override
+ public EclipseMethodCustomization addMethod(String method) {
+ return addMethod(method, null);
+ }
+
+ @Override
+ public EclipseMethodCustomization addMethod(String method, List importsToAdd) {
+ // Get the signature of the method.
+ Matcher methodSignatureMatcher = METHOD_SIGNATURE_PATTERN.matcher(method);
+ String methodSignature = null;
+ if (methodSignatureMatcher.find()) {
+ methodSignature = methodSignatureMatcher.group(1);
+ }
+
+ // find position
+ List fileLines = editor.getFileLines(fileName);
+ int lineNum = fileLines.size();
+ String currentLine = fileLines.get(--lineNum);
+ while (!currentLine.endsWith("}") || currentLine.startsWith("}")) {
+ currentLine = fileLines.get(--lineNum);
+ }
+
+ int indentAmount = Utils.getIndent(currentLine).length();
+
+ editor.insertBlankLine(fileName, ++lineNum, false);
+ Position newMethod = editor.insertBlankLineWithIndent(fileName, ++lineNum, indentAmount);
+
+ // replace
+ editor.replaceWithIndentedContent(fileName, newMethod, newMethod, method, newMethod.getCharacter());
+
+ final String mSig = (methodSignature == null) ? editor.getFileLine(fileName, lineNum) : methodSignature;
+
+ return EclipseUtils.addImports(importsToAdd, this, () -> getMethod(mSig));
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseClassCustomization removeMethod(String methodNameOrSignature) {
+ EclipseMethodCustomization methodCustomization = getMethod(methodNameOrSignature);
+
+ int methodSignatureLine = methodCustomization.getSymbol().getLocation().getRange().getStart().getLine();
+
+ // Begin by getting the method's Javadoc to determine where to begin removal of the method.
+ Position start = methodCustomization.getJavadoc().getJavadocRange().getStart();
+
+ // Find the ending location of the method being removed.
+ String bodyPositionFinder = editor.getFileLine(fileName, methodSignatureLine);
+ String methodBlockIndent = Utils.getIndent(bodyPositionFinder);
+
+ // Go until the beginning of the next method is found.
+ int endLine = Utils.walkDownFileUntilLineMatches(editor, fileName, methodSignatureLine,
+ lineContent -> lineContent.matches(methodBlockIndent + "."));
+ Position end = new Position(endLine, editor.getFileLine(fileName, endLine).length());
+
+ // Remove the method.
+ editor.replace(fileName, start, end, "");
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(fileUri);
+ fileEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
+
+ return this;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseClassCustomization rename(String newName) {
+ WorkspaceEdit workspaceEdit
+ = languageClient.renameSymbol(fileUri, symbol.getLocation().getRange().getStart(), newName);
+ List changes = new ArrayList<>();
+ for (Map.Entry> edit : workspaceEdit.getChanges().entrySet()) {
+ int i = edit.getKey().indexOf("src/main/java/");
+ String oldEntry = edit.getKey().substring(i);
+ if (editor.getContents().containsKey(oldEntry)) {
+ for (TextEdit textEdit : edit.getValue()) {
+ editor.replace(oldEntry, textEdit.getRange().getStart(), textEdit.getRange().getEnd(),
+ textEdit.getNewText());
+ }
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(edit.getKey());
+ if (oldEntry.endsWith("/" + className + ".java")) {
+ String newEntry = oldEntry.replace(className + ".java", newName + ".java");
+ editor.renameFile(oldEntry, newEntry);
+ String newUri = edit.getKey().replace(className + ".java", newName + ".java");
+ fileEvent.setType(FileChangeType.Deleted);
+ changes.add(fileEvent);
+ FileEvent newFile = new FileEvent();
+ newFile.setUri(newUri);
+ newFile.setType(FileChangeType.Created);
+ changes.add(newFile);
+ } else {
+ fileEvent.setType(FileChangeType.Changed);
+ changes.add(fileEvent);
+ }
+ }
+ }
+ languageClient.notifyWatchedFilesChanged(changes);
+
+ String packagePath = packageName.replace(".", "/");
+ Optional newClassSymbol = languageClient.findWorkspaceSymbol(newName)
+ .stream()
+ .filter(si -> si.getLocation().getUri().endsWith(packagePath + "/" + newName + ".java"))
+ .findFirst();
+ if (newClassSymbol.isEmpty()) {
+ throw new IllegalArgumentException("Renamed failed with new class " + newName + " not found.");
+ }
+ return new EclipseClassCustomization(editor, languageClient, packageName, newName, newClassSymbol.get());
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseClassCustomization setModifier(int modifiers) {
+ languageClient.listDocumentSymbols(symbol.getLocation().getUri())
+ .stream()
+ .filter(si -> si.getKind() == SymbolKind.Class && si.getName().equals(className))
+ .findFirst()
+ .ifPresent(symbolInformation -> EclipseUtils.replaceModifier(symbolInformation, editor, languageClient,
+ "(?:.+ )?class " + className, "class " + className, Modifier.classModifiers(), modifiers));
+
+ return refreshSymbol();
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseClassCustomization addAnnotation(String annotation) {
+ if (!annotation.startsWith("@")) {
+ annotation = "@" + annotation;
+ }
+
+ Optional symbol = languageClient.listDocumentSymbols(fileUri)
+ .stream()
+ .filter(si -> si.getKind() == SymbolKind.Class)
+ .findFirst();
+ if (symbol.isPresent()) {
+ if (editor.getContents().containsKey(fileName)) {
+ int line = symbol.get().getLocation().getRange().getStart().getLine();
+ Position position = editor.insertBlankLine(fileName, line, true);
+ editor.replace(fileName, position, position, annotation);
+
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(fileUri);
+ fileEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
+
+ EclipseUtils.organizeImportsOnRange(languageClient, editor, fileUri,
+ symbol.get().getLocation().getRange());
+ }
+ }
+
+ return refreshSymbol();
+ }
+
+ @SuppressWarnings("OptionalGetWithoutIsPresent")
+ @Override
+ public EclipseClassCustomization removeAnnotation(String annotation) {
+ return EclipseUtils.removeAnnotation(this,
+ compilationUnit -> compilationUnit.getClassByName(className)
+ .get()
+ .getAnnotationByName(Utils.cleanAnnotationName(annotation)),
+ this::refreshSymbol);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseClassCustomization renameEnumMember(String enumMemberName, String newName) {
+ String fileUri = symbol.getLocation().getUri();
+ String lowercaseEnumMemberName = enumMemberName.toLowerCase();
+
+ List edits = new ArrayList<>();
+ for (SymbolInformation si : languageClient.listDocumentSymbols(fileUri)) {
+ if (!si.getName().toLowerCase().contains(lowercaseEnumMemberName)) {
+ continue;
+ }
+
+ edits.add(languageClient.renameSymbol(fileUri, si.getLocation().getRange().getStart(), newName));
+ }
+ EclipseUtils.applyWorkspaceEdits(edits, editor, languageClient);
+ return this;
+ }
+
+ @Override
+ public EclipseClassCustomization customizeAst(Consumer astCustomization) {
+ CompilationUnit astToEdit = StaticJavaParser.parse(editor.getFileContent(fileName));
+ astCustomization.accept(astToEdit);
+ editor.replaceFile(fileName, astToEdit.toString());
+
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(fileUri);
+ fileEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
+
+ return refreshSymbol();
+ }
+
+ @Override
+ public EclipseClassCustomization refreshSymbol() {
+ return new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseCodeCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseCodeCustomization.java
new file mode 100644
index 00000000000..21183ab319c
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseCodeCustomization.java
@@ -0,0 +1,54 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.CodeCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import org.eclipse.lsp4j.SymbolInformation;
+
+/**
+ * Base class for all code based customizations.
+ */
+public abstract class EclipseCodeCustomization implements CodeCustomization {
+ final Editor editor;
+ final EclipseLanguageClient languageClient;
+ final SymbolInformation symbol;
+ final String fileUri;
+ final String fileName;
+
+ @SuppressWarnings("deprecation")
+ EclipseCodeCustomization(Editor editor, EclipseLanguageClient languageClient, SymbolInformation symbol) {
+ this.editor = editor;
+ this.languageClient = languageClient;
+ this.symbol = symbol;
+ this.fileUri = symbol.getLocation().getUri();
+ this.fileName = fileUri.substring(fileUri.indexOf("src/main/java/"));
+ }
+
+ @Override
+ public final Editor getEditor() {
+ return editor;
+ }
+
+ @Override
+ public final EclipseLanguageClient getLanguageClient() {
+ return languageClient;
+ }
+
+ @Override
+ public final SymbolInformation getSymbol() {
+ return symbol;
+ }
+
+ @Override
+ public final String getFileUri() {
+ return fileUri;
+ }
+
+ @Override
+ public final String getFileName() {
+ return fileName;
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseConstantCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseConstantCustomization.java
new file mode 100644
index 00000000000..3391adfde42
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseConstantCustomization.java
@@ -0,0 +1,138 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.ConstantCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.PropertyCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.SymbolKind;
+import org.eclipse.lsp4j.WorkspaceEdit;
+
+/**
+ * Customization for an AutoRest generated constant property.
+ *
+ * For instance property customizations use {@link PropertyCustomization}.
+ */
+public final class EclipseConstantCustomization extends EclipseCodeCustomization implements ConstantCustomization {
+ private static final Pattern METHOD_PARAMS_CAPTURE = Pattern.compile("\\(.*\\)");
+
+ private final String packageName;
+ private final String className;
+ private final String constantName;
+
+ EclipseConstantCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName,
+ String className, SymbolInformation symbol, String constantName) {
+ super(editor, languageClient, symbol);
+
+ this.packageName = packageName;
+ this.className = className;
+ this.constantName = constantName;
+ }
+
+ @Override
+ public String getClassName() {
+ return className;
+ }
+
+ @Override
+ public String getConstantName() {
+ return constantName;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseJavadocCustomization getJavadoc() {
+ return new EclipseJavadocCustomization(editor, languageClient, fileUri, fileName,
+ symbol.getLocation().getRange().getStart().getLine());
+ }
+
+ @Override
+ public EclipseConstantCustomization setModifier(int modifiers) {
+ EclipseUtils.replaceModifier(symbol, editor, languageClient, "(?:.+ )?(\\w+ )" + constantName + "\\(",
+ "$1" + constantName + "(", Modifier.fieldModifiers(), Modifier.STATIC | Modifier.FINAL | modifiers);
+
+ return refreshCustomization(constantName);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseConstantCustomization rename(String newName) {
+ Objects.requireNonNull(newName, "'newName' cannot be null.");
+
+ String lowercaseConstantName = constantName.toLowerCase();
+ String currentCamelName = constantToMethodName(constantName);
+ String lowercaseCurrentCamelName = currentCamelName.toLowerCase();
+ String newCamelName = constantToMethodName(newName);
+
+ List edits = new ArrayList<>();
+ for (SymbolInformation si : languageClient.listDocumentSymbols(fileUri)) {
+ String symbolName = si.getName().toLowerCase();
+ if (!symbolName.contains(lowercaseConstantName) && !symbolName.contains(lowercaseCurrentCamelName)) {
+ continue;
+ }
+
+ if (si.getKind() == SymbolKind.Constant) {
+ edits.add(languageClient.renameSymbol(fileUri, si.getLocation().getRange().getStart(), newName));
+ } else if (si.getKind() == SymbolKind.Method) {
+ String methodName = si.getName().replace(currentCamelName, newCamelName).replace(constantName, newName);
+ methodName = METHOD_PARAMS_CAPTURE.matcher(methodName).replaceFirst("");
+ edits.add(languageClient.renameSymbol(fileUri, si.getLocation().getRange().getStart(), methodName));
+ }
+ }
+
+ EclipseUtils.applyWorkspaceEdits(edits, editor, languageClient);
+ return refreshCustomization(newName);
+ }
+
+ private static String constantToMethodName(String constantName) {
+ // Constants will be in the form A_WORD_SPLIT_BY_UNDERSCORE_AND_CAPITALIZED, which, if used as-is won't follow
+ // getter, or method, naming conventions of getAWordInCamelCase.
+ //
+ // Split the constant name on '_' and lower case all characters after the first.
+ StringBuilder camelBuilder = new StringBuilder(constantName.length());
+
+ for (String word : constantName.split("_")) {
+ if (word.isEmpty()) {
+ continue;
+ }
+
+ camelBuilder.append(word.charAt(0));
+ if (word.length() > 1) {
+ camelBuilder.append(word.substring(1).toLowerCase());
+ }
+ }
+
+ return camelBuilder.toString();
+ }
+
+ @Override
+ public EclipseConstantCustomization addAnnotation(String annotation) {
+ return EclipseUtils.addAnnotation(annotation, this, () -> refreshCustomization(constantName));
+ }
+
+ @SuppressWarnings("OptionalGetWithoutIsPresent")
+ @Override
+ public EclipseConstantCustomization removeAnnotation(String annotation) {
+ return EclipseUtils.removeAnnotation(this,
+ compilationUnit -> compilationUnit.getClassByName(className)
+ .get()
+ .getFieldByName(constantName)
+ .get()
+ .getAnnotationByName(Utils.cleanAnnotationName(annotation)),
+ () -> refreshCustomization(constantName));
+ }
+
+ private EclipseConstantCustomization refreshCustomization(String constantName) {
+ return new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className)
+ .getConstant(constantName);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseConstructorCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseConstructorCustomization.java
new file mode 100644
index 00000000000..875bccfe809
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseConstructorCustomization.java
@@ -0,0 +1,112 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.ConstructorCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import java.lang.reflect.Modifier;
+import java.util.List;
+import org.eclipse.lsp4j.SymbolInformation;
+
+/**
+ * The constructor level customization for an AutoRest generated constructor.
+ */
+public final class EclipseConstructorCustomization extends EclipseCodeCustomization
+ implements ConstructorCustomization {
+ private final String packageName;
+ private final String className;
+ private final String constructorSignature;
+
+ EclipseConstructorCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName,
+ String className, String constructorSignature, SymbolInformation symbol) {
+ super(editor, languageClient, symbol);
+ this.packageName = packageName;
+ this.className = className;
+ this.constructorSignature = constructorSignature;
+ }
+
+ @Override
+ public String getClassName() {
+ return className;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseJavadocCustomization getJavadoc() {
+ return new EclipseJavadocCustomization(editor, languageClient, fileUri, fileName,
+ symbol.getLocation().getRange().getStart().getLine());
+ }
+
+ @Override
+ public EclipseConstructorCustomization addAnnotation(String annotation) {
+ return EclipseUtils.addAnnotation(annotation, this, () -> refreshCustomization(constructorSignature));
+ }
+
+ @SuppressWarnings({ "OptionalGetWithoutIsPresent", "deprecation" })
+ @Override
+ public EclipseConstructorCustomization removeAnnotation(String annotation) {
+ return EclipseUtils.removeAnnotation(this, compilationUnit -> compilationUnit.getClassByName(className)
+ .get()
+ .getConstructors()
+ .stream()
+ .filter(
+ ctor -> EclipseUtils.declarationContainsSymbol(ctor.getRange().get(), symbol.getLocation().getRange()))
+ .findFirst()
+ .get()
+ .getAnnotationByName(Utils.cleanAnnotationName(annotation)),
+ () -> refreshCustomization(constructorSignature));
+ }
+
+ @Override
+ public EclipseConstructorCustomization setModifier(int modifiers) {
+ EclipseUtils.replaceModifier(symbol, editor, languageClient, "(?:.+ )?" + className + "\\(", className + "(",
+ Modifier.constructorModifiers(), modifiers);
+
+ return refreshCustomization(constructorSignature);
+ }
+
+ @Override
+ public EclipseConstructorCustomization replaceParameters(String newParameters) {
+ return replaceParameters(newParameters, null);
+ }
+
+ @Override
+ public EclipseConstructorCustomization replaceParameters(String newParameters, List importsToAdd) {
+ String newSignature = className + "(" + newParameters + ")";
+
+ EclipseClassCustomization classCustomization
+ = new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className);
+
+ EclipseClassCustomization updatedClassCustomization
+ = EclipseUtils.addImports(importsToAdd, classCustomization, classCustomization::refreshSymbol);
+
+ return EclipseUtils.replaceParameters(newParameters,
+ updatedClassCustomization.getConstructor(constructorSignature),
+ () -> updatedClassCustomization.getConstructor(newSignature));
+ }
+
+ @Override
+ public EclipseConstructorCustomization replaceBody(String newBody) {
+ return replaceBody(newBody, null);
+ }
+
+ @Override
+ public EclipseConstructorCustomization replaceBody(String newBody, List importsToAdd) {
+ EclipseClassCustomization classCustomization
+ = new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className);
+
+ EclipseClassCustomization updatedClassCustomization
+ = EclipseUtils.addImports(importsToAdd, classCustomization, classCustomization::refreshSymbol);
+
+ return EclipseUtils.replaceBody(newBody, updatedClassCustomization.getConstructor(constructorSignature),
+ () -> updatedClassCustomization.getConstructor(constructorSignature));
+ }
+
+ private EclipseConstructorCustomization refreshCustomization(String constructorSignature) {
+ return new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className)
+ .getConstructor(constructorSignature);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseJavadocCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseJavadocCustomization.java
new file mode 100644
index 00000000000..0636119f830
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseJavadocCustomization.java
@@ -0,0 +1,371 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.JavadocCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
+
+/**
+ * The Javadoc customization for an AutoRest generated classes and methods.
+ */
+public final class EclipseJavadocCustomization implements JavadocCustomization {
+ /*
+ * This pattern attempts to cleanse a line of a Javadoc.
+ *
+ * The scenarios handled by this pattern are the following:
+ *
+ * 1. A single line Javadoc
+ * 2. An indented single line Javadoc
+ * 3. A part of a Javadoc
+ * 4. An indented part of a Javadoc
+ * 5. A Javadoc where the closing line contains text
+ * 6. An indented Javadoc where the closing line contains text
+ */
+ private static final Pattern JAVADOC_LINE_CLEANER = Pattern.compile("^\\s*/?\\*{1,2}\\s?(.*?)(?:\\s*\\*/)?$");
+
+ private static final Pattern EMPTY_JAVADOC_LINE_PATTERN = Pattern.compile("\\s*\\*/?\\s*");
+
+ private static final Pattern THROWS_TAG = Pattern.compile(".*@throws ");
+ private static final Pattern PARAM_TAG = Pattern.compile(".*@param ");
+ private static final Pattern SPACE_THEN_ANYTHING = Pattern.compile(" .*");
+ private static final Pattern JAVADOC_LINE_WITH_CONTENT = Pattern.compile("\\* .*$");
+ private static final Pattern END_JAVADOC_LINE = Pattern.compile(" \\*/$");
+ private static final Pattern JAVADOC_CONTENT = Pattern.compile(" +\\* ");
+
+ private final EclipseLanguageClient languageClient;
+ private final Editor editor;
+ private final String fileUri;
+ private final String fileName;
+ private final String indent;
+
+ private String descriptionDocs;
+ private final Map paramDocs;
+ private String returnDoc;
+ private final Map throwsDocs;
+ private final List seeDocs;
+ private String sinceDoc;
+ private String deprecatedDoc;
+ private Range javadocRange;
+
+ EclipseJavadocCustomization(Editor editor, EclipseLanguageClient languageClient, String fileUri, String fileName,
+ int symbolLine) {
+ this.editor = editor;
+ this.languageClient = languageClient;
+
+ this.paramDocs = new LinkedHashMap<>();
+ this.throwsDocs = new LinkedHashMap<>();
+ this.seeDocs = new ArrayList<>();
+
+ this.fileUri = fileUri;
+ this.fileName = fileName;
+
+ this.indent = Utils.getIndent(editor.getFileLine(fileName, symbolLine));
+ parseJavadoc(symbolLine);
+ }
+
+ @Override
+ public Range getJavadocRange() {
+ return javadocRange;
+ }
+
+ @Override
+ public EclipseJavadocCustomization replace(EclipseJavadocCustomization other) {
+ this.descriptionDocs = other.descriptionDocs;
+
+ this.paramDocs.clear();
+ if (other.paramDocs != null) {
+ this.paramDocs.putAll(other.paramDocs);
+ }
+
+ this.returnDoc = other.returnDoc;
+
+ this.throwsDocs.clear();
+ if (other.throwsDocs != null) {
+ this.throwsDocs.putAll(other.throwsDocs);
+ }
+
+ this.seeDocs.clear();
+ if (other.seeDocs != null) {
+ this.seeDocs.addAll(other.seeDocs);
+ }
+
+ this.sinceDoc = other.sinceDoc;
+ this.deprecatedDoc = other.deprecatedDoc;
+ commit();
+ return this;
+ }
+
+ @Override
+ public String getDescription() {
+ return descriptionDocs;
+ }
+
+ @Override
+ public EclipseJavadocCustomization setDescription(String description) {
+ return performChange(this.descriptionDocs, description, () -> this.descriptionDocs = description);
+ }
+
+ @Override
+ public Map getParams() {
+ return Collections.unmodifiableMap(paramDocs);
+ }
+
+ @Override
+ public EclipseJavadocCustomization setParam(String parameterName, String description) {
+ return performChange(paramDocs.get(parameterName), description,
+ () -> paramDocs.put(parameterName, description));
+ }
+
+ @Override
+ public EclipseJavadocCustomization removeParam(String parameterName) {
+ paramDocs.remove(parameterName);
+ commit();
+ return this;
+ }
+
+ @Override
+ public String getReturn() {
+ return returnDoc;
+ }
+
+ @Override
+ public EclipseJavadocCustomization setReturn(String description) {
+ return performChange(returnDoc, description, () -> this.returnDoc = description);
+ }
+
+ @Override
+ public EclipseJavadocCustomization removeReturn() {
+ return performChange(returnDoc, null, () -> this.returnDoc = null);
+ }
+
+ @Override
+ public Map getThrows() {
+ return Collections.unmodifiableMap(throwsDocs);
+ }
+
+ @Override
+ public EclipseJavadocCustomization addThrows(String exceptionType, String description) {
+ return performChange(throwsDocs.get(exceptionType), description,
+ () -> throwsDocs.put(exceptionType, description));
+ }
+
+ @Override
+ public EclipseJavadocCustomization removeThrows(String exceptionType) {
+ throwsDocs.remove(exceptionType);
+ commit();
+ return this;
+ }
+
+ @Override
+ public List getSees() {
+ return Collections.unmodifiableList(seeDocs);
+ }
+
+ @Override
+ public EclipseJavadocCustomization addSee(String seeDoc) {
+ seeDocs.add(seeDoc);
+ commit();
+ return this;
+ }
+
+ @Override
+ public String getSince() {
+ return sinceDoc;
+ }
+
+ @Override
+ public EclipseJavadocCustomization setSince(String sinceDoc) {
+ return performChange(this.sinceDoc, sinceDoc, () -> this.sinceDoc = sinceDoc);
+ }
+
+ @Override
+ public EclipseJavadocCustomization removeSince() {
+ return performChange(this.sinceDoc, null, () -> this.sinceDoc = null);
+ }
+
+ @Override
+ public String getDeprecated() {
+ return deprecatedDoc;
+ }
+
+ @Override
+ public EclipseJavadocCustomization setDeprecated(String deprecatedDoc) {
+ return performChange(this.deprecatedDoc, deprecatedDoc, () -> this.deprecatedDoc = deprecatedDoc);
+ }
+
+ @Override
+ public EclipseJavadocCustomization removeDeprecated() {
+ return performChange(this.deprecatedDoc, null, () -> this.deprecatedDoc = null);
+ }
+
+ private void initialize(int symbolLine) {
+ editor.insertBlankLine(fileName, symbolLine++, false);
+ editor.replace(fileName, new Position(symbolLine, 0), new Position(symbolLine, 0), indent);
+ Position javadocCursor = new Position(symbolLine, indent.length());
+ javadocRange = new Range(javadocCursor, javadocCursor);
+ ++symbolLine;
+ EclipseUtils.sendFilesChangeNotification(languageClient, fileUri);
+ }
+
+ private void parseJavadoc(int symbolLine) {
+ String lineContent = editor.getFileLine(fileName, --symbolLine);
+ while (lineContent.startsWith(indent + "@")) {
+ lineContent = editor.getFileLine(fileName, --symbolLine);
+ }
+ if (lineContent.endsWith("*/")) {
+ Position javadocEnd = new Position(symbolLine, lineContent.length());
+ int currentDocEndLine = symbolLine;
+ while (!lineContent.contains("/*")) {
+ if (lineContent.contains("@throws")) {
+ String type = THROWS_TAG.matcher(lineContent).replaceFirst("");
+ type = SPACE_THEN_ANYTHING.matcher(type).replaceFirst("");
+ Position docStart = new Position(symbolLine, lineContent.indexOf("@throws") + 8);
+ Position docEnd
+ = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
+ throwsDocs.put(type, readJavadocTextRange(editor, fileName, docStart, docEnd));
+ currentDocEndLine = symbolLine - 1;
+ } else if (lineContent.contains("@return")) {
+ Position docStart = new Position(symbolLine, lineContent.indexOf("@return") + 8);
+ Position docEnd
+ = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
+ returnDoc = readJavadocTextRange(editor, fileName, docStart, docEnd);
+ currentDocEndLine = symbolLine - 1;
+ } else if (lineContent.contains("@since")) {
+ Position docStart = new Position(symbolLine, lineContent.indexOf("@since") + 7);
+ Position docEnd
+ = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
+ sinceDoc = readJavadocTextRange(editor, fileName, docStart, docEnd);
+ currentDocEndLine = symbolLine - 1;
+ } else if (lineContent.contains("@see")) {
+ Position docStart = new Position(symbolLine, lineContent.indexOf("@see") + 5);
+ Position docEnd
+ = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
+ seeDocs.add(readJavadocTextRange(editor, fileName, docStart, docEnd));
+ currentDocEndLine = symbolLine - 1;
+ } else if (lineContent.contains("@deprecated")) {
+ Position docStart = new Position(symbolLine, lineContent.indexOf("@deprecated") + 5);
+ Position docEnd
+ = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
+ deprecatedDoc = readJavadocTextRange(editor, fileName, docStart, docEnd);
+ currentDocEndLine = symbolLine - 1;
+ } else if (lineContent.contains("@param")) {
+ String name = PARAM_TAG.matcher(lineContent).replaceFirst("");
+ name = SPACE_THEN_ANYTHING.matcher(name).replaceFirst("");
+ Position docStart = new Position(symbolLine, lineContent.indexOf("@param") + 8 + name.length());
+ Position docEnd
+ = new Position(currentDocEndLine, editor.getFileLine(fileName, currentDocEndLine).length());
+ paramDocs.put(name, readJavadocTextRange(editor, fileName, docStart, docEnd));
+ currentDocEndLine = symbolLine - 1;
+ } else if (EMPTY_JAVADOC_LINE_PATTERN.matcher(lineContent).matches()) {
+ // empty line
+ currentDocEndLine--;
+ }
+ lineContent = editor.getFileLine(fileName, --symbolLine);
+ }
+ Position javadocStart = new Position(symbolLine, indent.length());
+ javadocRange = new Range(javadocStart, javadocEnd);
+ if (lineContent.endsWith("/*") || lineContent.endsWith("/**")) {
+ symbolLine++;
+ }
+ Position descriptionStart = new Position(symbolLine,
+ JAVADOC_LINE_WITH_CONTENT.matcher(editor.getFileLine(fileName, symbolLine)).replaceFirst("").length()
+ + 2);
+ String descriptionEndLineContent = editor.getFileLine(fileName, currentDocEndLine);
+ while (descriptionEndLineContent.trim().endsWith("*")) {
+ descriptionEndLineContent = editor.getFileLine(fileName, --currentDocEndLine);
+ }
+ Position descriptionEnd = new Position(currentDocEndLine,
+ END_JAVADOC_LINE.matcher(descriptionEndLineContent).replaceFirst("").length());
+ this.descriptionDocs = JAVADOC_CONTENT
+ .matcher(editor.getTextInRange(fileName, new Range(descriptionStart, descriptionEnd), " "))
+ .replaceAll(" ")
+ .trim();
+ } else {
+ initialize(symbolLine);
+ }
+ }
+
+ private static String readJavadocTextRange(Editor editor, String fileName, Position docStart, Position docEnd) {
+ return editor.getTextInRange(fileName, new Range(docStart, docEnd), " ", line -> {
+ Matcher lineCleaningMatch = JAVADOC_LINE_CLEANER.matcher(line);
+ return (lineCleaningMatch.find()) ? lineCleaningMatch.group(1) : line;
+ }).trim();
+ }
+
+ private void commit() {
+ // Given this method is self-contained use StringBuilder as it doesn't have synchronization.
+ // Additional start with a sizeable 4kb buffer to reduce chances of resizing while keeping it small.
+ StringBuilder stringBuilder = new StringBuilder(4096);
+
+ Utils.writeLine(stringBuilder, "/**");
+ if (descriptionDocs != null) {
+ Utils.writeLine(stringBuilder.append(indent).append(" * "), descriptionDocs);
+ }
+
+ if (!paramDocs.isEmpty() || !throwsDocs.isEmpty() || returnDoc != null || deprecatedDoc != null) {
+ Utils.writeLine(stringBuilder.append(indent), " * ");
+
+ for (Map.Entry paramDoc : paramDocs.entrySet()) {
+ Utils.writeLine(stringBuilder.append(indent).append(" * @param ").append(paramDoc.getKey()).append(" "),
+ paramDoc.getValue());
+ }
+
+ if (returnDoc != null) {
+ Utils.writeLine(stringBuilder.append(indent).append(" * @return "), returnDoc);
+ }
+
+ for (Map.Entry throwsDoc : throwsDocs.entrySet()) {
+ Utils.writeLine(
+ stringBuilder.append(indent).append(" * @throws ").append(throwsDoc.getKey()).append(" "),
+ throwsDoc.getValue());
+ }
+
+ for (String seeDoc : seeDocs) {
+ Utils.writeLine(stringBuilder.append(indent).append(" * @see "), seeDoc);
+ }
+
+ if (sinceDoc != null) {
+ Utils.writeLine(stringBuilder.append(indent).append(" * @since "), sinceDoc);
+ }
+
+ if (deprecatedDoc != null) {
+ Utils.writeLine(stringBuilder.append(indent).append(" * @deprecated "), deprecatedDoc);
+ }
+
+ }
+
+ stringBuilder.append(indent).append(" */");
+
+ editor.replace(fileName, javadocRange.getStart(), javadocRange.getEnd(), stringBuilder.toString());
+ EclipseUtils.sendFilesChangeNotification(languageClient, fileUri);
+
+ int javadocStartLine = javadocRange.getStart().getLine();
+ String lineContent = editor.getFileLine(fileName, javadocStartLine);
+ while (!lineContent.endsWith("*/")) {
+ lineContent = editor.getFileLine(fileName, ++javadocStartLine);
+ }
+ parseJavadoc(javadocStartLine + 1);
+ }
+
+ private EclipseJavadocCustomization performChange(String oldValue, String newValue, Runnable changePerformer) {
+ if (!Objects.equals(oldValue, newValue)) {
+ changePerformer.run();
+ commit();
+ }
+
+ return this;
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseLibraryCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseLibraryCustomization.java
new file mode 100644
index 00000000000..3281d9745d6
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseLibraryCustomization.java
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.LibraryCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import java.util.Optional;
+import org.eclipse.lsp4j.SymbolInformation;
+
+/**
+ * Implementation of {@link LibraryCustomization} that uses the Eclipse Language Server for customizations.
+ */
+public final class EclipseLibraryCustomization implements LibraryCustomization {
+ private final Editor editor;
+ private final EclipseLanguageClient languageClient;
+
+ public EclipseLibraryCustomization(Editor editor, EclipseLanguageClient languageClient) {
+ this.editor = editor;
+ this.languageClient = languageClient;
+ }
+
+ @Override
+ public EclipsePackageCustomization getPackage(String packageName) {
+ return new EclipsePackageCustomization(editor, languageClient, packageName);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseClassCustomization getClass(String packageName, String className) {
+ String packagePath = packageName.replace(".", "/");
+ Optional classSymbol = languageClient.findWorkspaceSymbol(className)
+ .stream()
+ // findWorkspace symbol finds all classes that contain the classname term
+ // The filter that checks the filename only works if there are no nested classes
+ // So, when customizing client classes that contain service interface, this can incorrectly return
+ // the service interface instead of the client class. So, we should add another check for exact name match
+ .filter(si -> si.getName().equals(className))
+ .filter(si -> si.getLocation().getUri().endsWith(packagePath + "/" + className + ".java"))
+ .findFirst();
+
+ return Utils.returnIfPresentOrThrow(classSymbol,
+ symbol -> new EclipseClassCustomization(getRawEditor(), languageClient, packageName, className, symbol),
+ () -> new IllegalArgumentException(className + " does not exist in package " + packageName));
+ }
+
+ @Override
+ public Editor getRawEditor() {
+ return editor;
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseMethodCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseMethodCustomization.java
new file mode 100644
index 00000000000..732b2e28e77
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseMethodCustomization.java
@@ -0,0 +1,227 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.MethodCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.eclipse.lsp4j.FileChangeType;
+import org.eclipse.lsp4j.FileEvent;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+
+/**
+ * The method level customization for an AutoRest generated method.
+ */
+public final class EclipseMethodCustomization extends EclipseCodeCustomization implements MethodCustomization {
+ private final String packageName;
+ private final String className;
+ private final String methodName;
+ private final String methodSignature;
+
+ EclipseMethodCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName,
+ String className, String methodName, String methodSignature, SymbolInformation symbol) {
+ super(editor, languageClient, symbol);
+ this.packageName = packageName;
+ this.className = className;
+ this.methodName = methodName;
+ this.methodSignature = methodSignature;
+ }
+
+ @Override
+ public String getMethodName() {
+ return methodName;
+ }
+
+ @Override
+ public String getClassName() {
+ return className;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseJavadocCustomization getJavadoc() {
+ return new EclipseJavadocCustomization(editor, languageClient, fileUri, fileName,
+ symbol.getLocation().getRange().getStart().getLine());
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseMethodCustomization rename(String newName) {
+ WorkspaceEdit edit = languageClient.renameSymbol(fileUri, symbol.getLocation().getRange().getStart(), newName);
+ EclipseUtils.applyWorkspaceEdit(edit, editor, languageClient);
+
+ return refreshCustomization(methodSignature.replace(methodName + "(", newName + "("));
+ }
+
+ @Override
+ public EclipseMethodCustomization addAnnotation(String annotation) {
+ return EclipseUtils.addAnnotation(annotation, this, () -> refreshCustomization(methodSignature));
+ }
+
+ @SuppressWarnings({ "OptionalGetWithoutIsPresent", "deprecation" })
+ @Override
+ public EclipseMethodCustomization removeAnnotation(String annotation) {
+ return EclipseUtils.removeAnnotation(this,
+ compilationUnit -> compilationUnit.getClassByName(className)
+ .get()
+ .getMethodsByName(methodName)
+ .stream()
+ .filter(method -> EclipseUtils.declarationContainsSymbol(method.getRange().get(),
+ symbol.getLocation().getRange()))
+ .findFirst()
+ .get()
+ .getAnnotationByName(Utils.cleanAnnotationName(annotation)),
+ () -> refreshCustomization(methodSignature));
+ }
+
+ @Override
+ public EclipseMethodCustomization setModifier(int modifiers) {
+ EclipseUtils.replaceModifier(symbol, editor, languageClient, "(?:.+ )?(\\w+ )" + methodName + "\\(",
+ "$1" + methodName + "(", Modifier.methodModifiers(), modifiers);
+
+ return refreshCustomization(methodSignature);
+ }
+
+ @Override
+ public EclipseMethodCustomization replaceParameters(String newParameters) {
+ return replaceParameters(newParameters, null);
+ }
+
+ @Override
+ public EclipseMethodCustomization replaceParameters(String newParameters, List importsToAdd) {
+ String newSignature = methodName + "(" + newParameters + ")";
+
+ EclipseClassCustomization classCustomization
+ = new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className);
+
+ EclipseClassCustomization updatedClassCustomization
+ = EclipseUtils.addImports(importsToAdd, classCustomization, classCustomization::refreshSymbol);
+
+ return EclipseUtils.replaceParameters(newParameters, updatedClassCustomization.getMethod(methodSignature),
+ () -> updatedClassCustomization.getMethod(newSignature));
+ }
+
+ @Override
+ public EclipseMethodCustomization replaceBody(String newBody) {
+ return replaceBody(newBody, null);
+ }
+
+ @Override
+ public EclipseMethodCustomization replaceBody(String newBody, List importsToAdd) {
+ EclipseClassCustomization classCustomization
+ = new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className);
+
+ EclipseClassCustomization updatedClassCustomization
+ = EclipseUtils.addImports(importsToAdd, classCustomization, classCustomization::refreshSymbol);
+
+ return EclipseUtils.replaceBody(newBody, updatedClassCustomization.getMethod(methodSignature),
+ () -> updatedClassCustomization.getMethod(methodSignature));
+ }
+
+ @Override
+ public EclipseMethodCustomization setReturnType(String newReturnType, String returnValueFormatter) {
+ return setReturnType(newReturnType, returnValueFormatter, false);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public EclipseMethodCustomization setReturnType(String newReturnType, String returnValueFormatter,
+ boolean replaceReturnStatement) {
+ List edits = new ArrayList<>();
+
+ int line = symbol.getLocation().getRange().getStart().getLine();
+ Position start = new Position(line, 0);
+ String oldLineContent = editor.getFileLine(fileName, line);
+ Position end = new Position(line, oldLineContent.length());
+ String newLineContent = oldLineContent.replaceFirst("(\\w.* )?(\\w+) " + methodName + "\\(",
+ "$1" + newReturnType + " " + methodName + "(");
+ TextEdit signatureEdit = new TextEdit();
+ signatureEdit.setNewText(newLineContent);
+ signatureEdit.setRange(new Range(start, end));
+ edits.add(signatureEdit);
+
+ String methodIndent = Utils.getIndent(editor.getFileLine(fileName, line));
+ String methodContentIndent = Utils.getIndent(editor.getFileLine(fileName, line + 1));
+ String oldReturnType = oldLineContent.replaceAll(" " + methodName + "\\(.*", "")
+ .replaceFirst(methodIndent + "(\\w.* )?", "")
+ .trim();
+ int returnLine = -1;
+ while (!oldLineContent.startsWith(methodIndent + "}")) {
+ if (oldLineContent.contains("return ")) {
+ returnLine = line;
+ }
+ oldLineContent = editor.getFileLine(fileName, ++line);
+ }
+ if (returnLine == -1) {
+ // no return statement, originally void return type
+ editor.insertBlankLine(fileName, line, false);
+ FileEvent blankLineEvent = new FileEvent();
+ blankLineEvent.setUri(fileUri);
+ blankLineEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(blankLineEvent));
+
+ TextEdit returnEdit = new TextEdit();
+ returnEdit.setRange(new Range(new Position(line, 0), new Position(line, 0)));
+ returnEdit.setNewText(methodContentIndent + "return " + returnValueFormatter + ";");
+ edits.add(returnEdit);
+ } else if (newReturnType.equals("void")) {
+ // remove return statement
+ TextEdit returnEdit = new TextEdit();
+ returnEdit.setNewText("");
+ returnEdit.setRange(new Range(new Position(returnLine, 0), new Position(line, 0)));
+ edits.add(returnEdit);
+ } else {
+ // replace return statement
+ TextEdit returnValueEdit = new TextEdit();
+ String returnLineText = editor.getFileLine(fileName, returnLine);
+ returnValueEdit
+ .setRange(new Range(new Position(returnLine, 0), new Position(returnLine, returnLineText.length())));
+ returnValueEdit.setNewText(returnLineText.replace("return ", oldReturnType + " returnValue = "));
+ edits.add(returnValueEdit);
+
+ editor.insertBlankLine(fileName, line, false);
+ FileEvent blankLineEvent = new FileEvent();
+ blankLineEvent.setUri(fileUri);
+ blankLineEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(blankLineEvent));
+
+ TextEdit returnEdit = new TextEdit();
+ returnEdit.setRange(new Range(new Position(line, 0), new Position(line, 0)));
+
+ if (replaceReturnStatement) {
+ returnEdit.setNewText(String.format(returnValueFormatter, "returnValue"));
+ } else {
+ returnEdit.setNewText(
+ methodContentIndent + "return " + String.format(returnValueFormatter, "returnValue") + ";");
+ }
+
+ edits.add(returnEdit);
+ }
+
+ WorkspaceEdit workspaceEdit = new WorkspaceEdit();
+ workspaceEdit.setChanges(Collections.singletonMap(fileUri, edits));
+ EclipseUtils.applyWorkspaceEdit(workspaceEdit, editor, languageClient);
+
+ EclipseUtils.organizeImportsOnRange(languageClient, editor, fileUri, new Range(start, end));
+
+ String newMethodSignature
+ = methodSignature.replace(oldReturnType + " " + methodName, newReturnType + " " + methodName);
+
+ return refreshCustomization(newMethodSignature);
+ }
+
+ private EclipseMethodCustomization refreshCustomization(String methodSignature) {
+ return new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className)
+ .getMethod(methodSignature);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipsePackageCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipsePackageCustomization.java
new file mode 100644
index 00000000000..61da08b04b8
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipsePackageCustomization.java
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.ClassCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.PackageCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.eclipse.lsp4j.SymbolInformation;
+
+/**
+ * The package level customization for an AutoRest generated client library.
+ */
+public final class EclipsePackageCustomization implements PackageCustomization {
+ private final EclipseLanguageClient languageClient;
+ private final Editor editor;
+ private final String packageName;
+
+ EclipsePackageCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName) {
+ this.editor = editor;
+ this.languageClient = languageClient;
+ this.packageName = packageName;
+ }
+
+ /**
+ * Gets the class level customization for a Java class in the package.
+ *
+ * @param className the simple name of the class
+ * @return the class level customization
+ */
+ @SuppressWarnings("deprecation")
+ public EclipseClassCustomization getClass(String className) {
+ String packagePath = packageName.replace(".", "/");
+ Optional classSymbol = languageClient.findWorkspaceSymbol(className)
+ .stream()
+ // findWorkspace symbol finds all classes that contain the classname term
+ // The filter that checks the filename only works if there are no nested classes
+ // So, when customizing client classes that contain service interface, this can incorrectly return
+ // the service interface instead of the client class. So, we should add another check for exact name match
+ .filter(si -> si.getName().equals(className))
+ .filter(si -> si.getLocation().getUri().endsWith(packagePath + "/" + className + ".java"))
+ .findFirst();
+
+ return Utils.returnIfPresentOrThrow(classSymbol,
+ symbol -> new EclipseClassCustomization(editor, languageClient, packageName, className, symbol),
+ () -> new IllegalArgumentException(className + " does not exist in package " + packageName));
+ }
+
+ /**
+ * This method lists all the classes in this package.
+ *
+ * @return A list of classes that are in this package.
+ */
+ @SuppressWarnings("deprecation")
+ public List listClasses() {
+ return languageClient.findWorkspaceSymbol("*")
+ .stream()
+ .filter(si -> si.getContainerName().equals(packageName))
+ .map(classSymbol -> new EclipseClassCustomization(editor, languageClient, packageName,
+ classSymbol.getName(), classSymbol))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipsePropertyCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipsePropertyCustomization.java
new file mode 100644
index 00000000000..6fd11d421ed
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipsePropertyCustomization.java
@@ -0,0 +1,176 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.ConstantCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.PropertyCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.models.JavaCodeActionKind;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.SymbolKind;
+import org.eclipse.lsp4j.WorkspaceEdit;
+
+/**
+ * Customization for an AutoRest generated instance property.
+ *
+ * For constant property customizations use {@link ConstantCustomization}.
+ */
+public final class EclipsePropertyCustomization extends EclipseCodeCustomization implements PropertyCustomization {
+ private static final Pattern METHOD_PARAMS_CAPTURE = Pattern.compile("\\(.*\\)");
+
+ private final String packageName;
+ private final String className;
+ private final String propertyName;
+
+ EclipsePropertyCustomization(Editor editor, EclipseLanguageClient languageClient, String packageName,
+ String className, SymbolInformation symbol, String propertyName) {
+ super(editor, languageClient, symbol);
+ this.packageName = packageName;
+ this.className = className;
+ this.propertyName = propertyName;
+ }
+
+ /**
+ * Gets the name of the class that contains this property.
+ *
+ * @return The name of the class that contains this property.
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Gets the name of this property.
+ *
+ * @return The name of this property.
+ */
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * Rename a property in the class. This is a refactor operation. All references of the property will be renamed and
+ * the getter and setter method(s) for this property will be renamed accordingly as well.
+ *
+ * @param newName the new name for the property
+ * @return the current class customization for chaining
+ */
+ @SuppressWarnings("deprecation")
+ public EclipsePropertyCustomization rename(String newName) {
+ List symbols = languageClient.listDocumentSymbols(fileUri)
+ .stream()
+ .filter(si -> si.getName().toLowerCase().contains(propertyName.toLowerCase()))
+ .collect(Collectors.toList());
+ String propertyPascalName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
+ String newPascalName = newName.substring(0, 1).toUpperCase() + newName.substring(1);
+
+ List edits = new ArrayList<>();
+ for (SymbolInformation symbol : symbols) {
+ if (symbol.getKind() == SymbolKind.Field) {
+ edits.add(languageClient.renameSymbol(fileUri, symbol.getLocation().getRange().getStart(), newName));
+ } else if (symbol.getKind() == SymbolKind.Method) {
+ String methodName
+ = symbol.getName().replace(propertyPascalName, newPascalName).replace(propertyName, newName);
+ methodName = METHOD_PARAMS_CAPTURE.matcher(methodName).replaceFirst("");
+ edits.add(languageClient.renameSymbol(fileUri, symbol.getLocation().getRange().getStart(), methodName));
+ }
+ }
+
+ EclipseUtils.applyWorkspaceEdits(edits, editor, languageClient);
+ return refreshCustomization(newName);
+ }
+
+ /**
+ * Add an annotation to a property in the class.
+ *
+ * @param annotation the annotation to add. The leading @ can be omitted.
+ * @return the current property customization for chaining
+ */
+ public EclipsePropertyCustomization addAnnotation(String annotation) {
+ return EclipseUtils.addAnnotation(annotation, this, () -> refreshCustomization(propertyName));
+ }
+
+ /**
+ * Remove an annotation from the property.
+ *
+ * @param annotation the annotation to remove from the property. The leading @ can be omitted.
+ * @return the current property customization for chaining
+ */
+ @SuppressWarnings("OptionalGetWithoutIsPresent")
+ public EclipsePropertyCustomization removeAnnotation(String annotation) {
+ return EclipseUtils.removeAnnotation(this,
+ compilationUnit -> compilationUnit.getClassByName(className)
+ .get()
+ .getFieldByName(propertyName)
+ .get()
+ .getAnnotationByName(Utils.cleanAnnotationName(annotation)),
+ () -> refreshCustomization(propertyName));
+ }
+
+ /**
+ * Generates a getter and a setter method(s) for a property in the class. This is a refactor operation. If a getter
+ * or a setter is already available on the class, the current getter or setter will be kept.
+ *
+ * @return the current class customization for chaining
+ */
+ @SuppressWarnings("deprecation")
+ public EclipsePropertyCustomization generateGetterAndSetter() {
+ Optional generateAccessors = languageClient
+ .listCodeActions(fileUri, symbol.getLocation().getRange(),
+ JavaCodeActionKind.SOURCE_GENERATE_ACCESSORS.toString())
+ .stream()
+ .filter(ca -> ca.getKind().equals(JavaCodeActionKind.SOURCE_GENERATE_ACCESSORS.toString()))
+ .findFirst();
+ if (generateAccessors.isPresent()) {
+ EclipseUtils.applyWorkspaceEdit(generateAccessors.get().getEdit(), editor, languageClient);
+
+ String setterMethod = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
+ new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className)
+ .getMethod(setterMethod)
+ .setReturnType(className, "this");
+ }
+
+ return this;
+ }
+
+ /**
+ * Replace the modifier for this property.
+ *
+ * For compound modifiers such as {@code public final} use bitwise OR ({@code |}) of multiple Modifiers, {@code
+ * Modifier.PUBLIC | Modifier.FINAL}.
+ *
+ * Pass {@code 0} for {@code modifiers} to indicate that the property has no modifiers.
+ *
+ * @param modifiers The {@link Modifier Modifiers} for the property.
+ * @return The updated PropertyCustomization object.
+ * @throws IllegalArgumentException If the {@code modifier} is less than {@code 0} or any {@link Modifier} included
+ * in the bitwise OR isn't a valid property {@link Modifier}.
+ */
+ @SuppressWarnings("deprecation")
+ public EclipsePropertyCustomization setModifier(int modifiers) {
+ String target = " *(?:(?:public|protected|private|static|final|transient|volatile) ?)*(.* )";
+ languageClient.listDocumentSymbols(symbol.getLocation().getUri())
+ .stream()
+ .filter(si -> si.getKind() == SymbolKind.Field && si.getName().equals(propertyName))
+ .findFirst()
+ .ifPresent(symbolInformation -> EclipseUtils.replaceModifier(symbolInformation, editor, languageClient,
+ target + propertyName, "$1" + propertyName, Modifier.fieldModifiers(), modifiers));
+
+ return refreshCustomization(propertyName);
+ }
+
+ private EclipsePropertyCustomization refreshCustomization(String propertyName) {
+ return new EclipsePackageCustomization(editor, languageClient, packageName).getClass(className)
+ .getProperty(propertyName);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseUtils.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseUtils.java
new file mode 100644
index 00000000000..af3634111e5
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/eclipsecustomization/EclipseUtils.java
@@ -0,0 +1,451 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.eclipsecustomization;
+
+import com.github.javaparser.StaticJavaParser;
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.expr.AnnotationExpr;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.eclipse.lsp4j.CodeActionKind;
+import org.eclipse.lsp4j.FileChangeType;
+import org.eclipse.lsp4j.FileEvent;
+import org.eclipse.lsp4j.Position;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+
+public class EclipseUtils {
+ /**
+ * This pattern determines the indentation of the passed string. Effectively it creates a group containing all
+ * spaces before the first word character.
+ */
+ public static final Pattern INDENT_DETERMINATION_PATTERN = Pattern.compile("^(\\s*).*$");
+
+ /**
+ * This pattern matches a Java package declaration.
+ */
+ private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s[\\w.]+;");
+
+ /**
+ * This pattern matches anything then the space.
+ */
+ public static final Pattern ANYTHING_THEN_SPACE_PATTERN = Pattern.compile(".* ");
+
+ /*
+ * This pattern determines if a line is a beginning of constructor or method. The following is an explanation of
+ * the pattern:
+ *
+ * 1. Capture all leading space characters.
+ * 2. Capture all modifiers for the constructor or method.
+ * 3. If a method, capture the return type.
+ * 4. Capture the name of the constructor or method.
+ *
+ * The following are the groups return:
+ *
+ * 1. The entire matching declaration from the beginning of the string used to determine the beginning offset of the
+ * parameters in the constructor or method.
+ * 2. Any modifiers for the constructor or method. This may be empty/null.
+ * 3. If a method, the return type. If a constructor, empty/null.
+ * 4. The name of the constructor or method.
+ */
+ private static final Pattern BEGINNING_OF_PARAMETERS_PATTERN
+ = Pattern.compile("^(\\s*(?:([\\w\\s]*?)\\s)?(?:([a-zA-Z$_][\\w]*?)\\s+)?([a-zA-Z$_][\\w]*?)\\s*)\\(.*$");
+
+ private static final Pattern ENDING_OF_PARAMETERS_PATTERN = Pattern.compile("^(.*)\\)\\s*\\{.*$");
+
+ public static void applyWorkspaceEdit(WorkspaceEdit workspaceEdit, Editor editor,
+ EclipseLanguageClient languageClient) {
+ Map changes = new HashMap<>();
+ applyWorkspaceEditInternal(workspaceEdit.getChanges(), changes, editor);
+ languageClient.notifyWatchedFilesChanged(new ArrayList<>(changes.values()));
+ }
+
+ public static void applyWorkspaceEdits(List workspaceEdits, Editor editor,
+ EclipseLanguageClient languageClient) {
+ if (workspaceEdits == null || workspaceEdits.isEmpty()) {
+ return;
+ }
+
+ Map changes = new HashMap<>();
+ for (WorkspaceEdit workspaceEdit : workspaceEdits) {
+ applyWorkspaceEditInternal(workspaceEdit.getChanges(), changes, editor);
+ }
+
+ languageClient.notifyWatchedFilesChanged(new ArrayList<>(changes.values()));
+ }
+
+ private static void applyWorkspaceEditInternal(Map> edits, Map changes,
+ Editor editor) {
+ if (edits == null || edits.isEmpty()) {
+ return;
+ }
+
+ for (Map.Entry> edit : edits.entrySet()) {
+ int i = edit.getKey().indexOf("src/main/java/");
+ String fileName = edit.getKey().substring(i);
+ if (editor.getContents().containsKey(fileName)) {
+ for (TextEdit textEdit : edit.getValue()) {
+ editor.replace(fileName, textEdit.getRange().getStart(), textEdit.getRange().getEnd(),
+ textEdit.getNewText());
+ }
+ changes.putIfAbsent(fileName, new FileEvent(edit.getKey(), FileChangeType.Changed));
+ }
+ }
+ }
+
+ public static boolean isNullOrEmpty(CharSequence charSequence) {
+ return charSequence == null || charSequence.length() == 0;
+ }
+
+ public static boolean isNullOrEmpty(T[] array) {
+ return array == null || array.length == 0;
+ }
+
+ public static boolean isNullOrEmpty(Iterable iterable) {
+ return (iterable == null || !iterable.iterator().hasNext());
+ }
+
+ static void validateModifiers(int validTypeModifiers, int newModifiers) {
+ // 0 indicates no modifiers.
+ if (newModifiers == 0) {
+ return;
+ }
+
+ if (newModifiers < 0) {
+ throw new IllegalArgumentException("Modifiers aren't allowed to be less than or equal to 0.");
+ }
+
+ if (validTypeModifiers != (validTypeModifiers | newModifiers)) {
+ throw new IllegalArgumentException("Modifiers contain illegal modifiers for the type.");
+ }
+ }
+
+ /**
+ * Replaces the modifier for a given symbol.
+ *
+ * @param symbol The symbol having its modifier replaced.
+ * @param editor The editor containing information about the symbol.
+ * @param languageClient The language client handling replacement of the modifiers.
+ * @param replaceTarget A string regex that determines how the modifiers are replaced.
+ * @param modifierReplaceBase A string that determines the base modifier replacement.
+ * @param validaTypeModifiers The modifier bit flag used to validate the new modifiers.
+ * @param newModifiers The new modifiers for the symbol.
+ */
+ @SuppressWarnings("deprecation")
+ public static void replaceModifier(SymbolInformation symbol, Editor editor, EclipseLanguageClient languageClient,
+ String replaceTarget, String modifierReplaceBase, int validaTypeModifiers, int newModifiers) {
+ validateModifiers(validaTypeModifiers, newModifiers);
+
+ String fileUri = symbol.getLocation().getUri();
+ int i = fileUri.indexOf("src/main/java/");
+ String fileName = fileUri.substring(i);
+
+ int line = symbol.getLocation().getRange().getStart().getLine();
+ Position start = new Position(line, 0);
+ String oldLineContent = editor.getFileLine(fileName, line);
+ Position end = new Position(line, oldLineContent.length());
+
+ String newModifiersString = Modifier.toString(newModifiers);
+ String newLineContent = (isNullOrEmpty(newModifiersString))
+ ? oldLineContent.replaceFirst(replaceTarget, modifierReplaceBase)
+ : oldLineContent.replaceFirst(replaceTarget, newModifiersString + " " + modifierReplaceBase);
+
+ TextEdit textEdit = new TextEdit();
+ textEdit.setNewText(newLineContent);
+ textEdit.setRange(new Range(start, end));
+ WorkspaceEdit workspaceEdit = new WorkspaceEdit();
+ workspaceEdit.setChanges(Collections.singletonMap(fileUri, Collections.singletonList(textEdit)));
+ EclipseUtils.applyWorkspaceEdit(workspaceEdit, editor, languageClient);
+ }
+
+ /**
+ * Walks down the lines of a file until the line matches a predicate.
+ *
+ * @param editor The editor containing the file's information.
+ * @param fileName The name of the file.
+ * @param startLine The line to start walking.
+ * @param linePredicate The predicate that determines when a matching line is found.
+ * @return The first line that matches the predicate. If no line in the file matches the predicate {@code -1} is
+ * returned.
+ */
+ public static int walkDownFileUntilLineMatches(Editor editor, String fileName, int startLine,
+ Predicate linePredicate) {
+ return walkFileUntilLineMatches(editor, fileName, startLine, linePredicate, true);
+ }
+
+ private static int walkFileUntilLineMatches(Editor editor, String fileName, int startLine,
+ Predicate linePredicate, boolean isWalkingDown) {
+ int matchingLine = -1;
+
+ List fileLines = editor.getFileLines(fileName);
+ if (isWalkingDown) {
+ for (int line = startLine; line < fileLines.size(); line++) {
+ if (linePredicate.test(fileLines.get(line))) {
+ matchingLine = line;
+ break;
+ }
+ }
+ } else {
+ for (int line = startLine; line >= 0; line--) {
+ if (linePredicate.test(fileLines.get(line))) {
+ matchingLine = line;
+ break;
+ }
+ }
+ }
+
+ return matchingLine;
+ }
+
+ /**
+ * Utility method to add an annotation to a code block.
+ *
+ * @param annotation The annotation to add.
+ * @param customization The customization having an annotation added.
+ * @param refreshedCustomizationSupplier A supplier that returns a refreshed customization after the annotation is
+ * added.
+ * @param The type of the customization.
+ * @return A refreshed customization after the annotation was added.
+ */
+ @SuppressWarnings("deprecation")
+ public static T addAnnotation(String annotation,
+ EclipseCodeCustomization customization, Supplier refreshedCustomizationSupplier) {
+ SymbolInformation symbol = customization.getSymbol();
+ Editor editor = customization.getEditor();
+ String fileName = customization.getFileName();
+ String fileUri = customization.getFileUri();
+ EclipseLanguageClient languageClient = customization.getLanguageClient();
+
+ if (!annotation.startsWith("@")) {
+ annotation = "@" + annotation;
+ }
+
+ if (editor.getContents().containsKey(fileName)) {
+ int line = symbol.getLocation().getRange().getStart().getLine();
+ Position position = editor.insertBlankLine(fileName, line, true);
+ editor.replace(fileName, position, position, annotation);
+
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(fileUri);
+ fileEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
+
+ organizeImportsOnRange(languageClient, editor, fileUri, symbol.getLocation().getRange());
+ }
+
+ return refreshedCustomizationSupplier.get();
+ }
+
+ /**
+ * Utility method to remove an annotation from a code block.
+ *
+ * @param codeCustomization The customization having an annotation removed.
+ * @param annotationRetriever Function that retrieves the potential annotation.
+ * @param refreshedCustomizationSupplier Supplier that returns a refreshed customization after the annotation is
+ * removed.
+ * @param The type of the customization.
+ * @return A refreshed customization if the annotation was removed, otherwise the customization as-is.
+ */
+ public static T removeAnnotation(T codeCustomization,
+ Function> annotationRetriever,
+ Supplier refreshedCustomizationSupplier) {
+ Editor editor = codeCustomization.getEditor();
+ String fileName = codeCustomization.getFileName();
+
+ CompilationUnit compilationUnit = StaticJavaParser.parse(editor.getFileContent(fileName));
+ Optional potentialAnnotation = annotationRetriever.apply(compilationUnit);
+
+ if (potentialAnnotation.isPresent()) {
+ potentialAnnotation.get().remove();
+ editor.replaceFile(fileName, compilationUnit.toString());
+ EclipseUtils.sendFilesChangeNotification(codeCustomization.getLanguageClient(),
+ codeCustomization.getFileUri());
+ return refreshedCustomizationSupplier.get();
+ } else {
+ return codeCustomization;
+ }
+ }
+
+ /**
+ * Notifies watchers of a file that it has changed.
+ *
+ * @param languageClient The {@link EclipseLanguageClient} sending the file changed notification.
+ * @param fileUri The URI of the file that was changed.
+ */
+ public static void sendFilesChangeNotification(EclipseLanguageClient languageClient, String fileUri) {
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(fileUri);
+ fileEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
+ }
+
+ public static boolean declarationContainsSymbol(com.github.javaparser.Range declarationRange, Range symbolRange) {
+ return declarationRange.begin.line <= symbolRange.getStart().getLine()
+ && declarationRange.end.line >= symbolRange.getStart().getLine();
+ }
+
+ /**
+ * Utility method to replace a body of a code block.
+ *
+ * @param newBody The new body.
+ * @param customization The customization having its body replaced.
+ * @param refreshedCustomizationSupplier A supplier that returns a refreshed customization after the body is
+ * replaced.
+ * @param The type of the customization.
+ * @return A refreshed customization after the body was replaced.
+ */
+ @SuppressWarnings("deprecation")
+ public static T replaceBody(String newBody,
+ EclipseCodeCustomization customization, Supplier refreshedCustomizationSupplier) {
+ SymbolInformation symbol = customization.getSymbol();
+ Editor editor = customization.getEditor();
+ String fileName = customization.getFileName();
+
+ int line = symbol.getLocation().getRange().getStart().getLine();
+ String methodBlockIndent = getIndent(editor.getFileLine(fileName, line));
+
+ // Loop until the line containing the body start is found.
+ Pattern startPattern = Pattern.compile(".*\\{\\s*");
+ int startLine = walkDownFileUntilLineMatches(editor, fileName, line,
+ lineContent -> startPattern.matcher(lineContent).matches()) + 1; // Plus one since the start is after the
+ // opening '{'
+
+ // Then determine the base indentation level for the body.
+ String methodContentIndent = getIndent(editor.getFileLine(fileName, startLine));
+ Position oldBodyStart = new Position(startLine, methodContentIndent.length());
+
+ // Then continue iterating over lines until the body close line is found.
+ Pattern closePattern = Pattern.compile(methodBlockIndent + "}\\s*");
+ int lastLine = walkDownFileUntilLineMatches(editor, fileName, startLine,
+ lineContent -> closePattern.matcher(lineContent).matches()) - 1; // Minus one since the end is before the
+ // closing '}'
+ Position oldBodyEnd = new Position(lastLine, editor.getFileLine(fileName, lastLine).length());
+
+ editor.replaceWithIndentedContent(fileName, oldBodyStart, oldBodyEnd, newBody, methodContentIndent.length());
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(customization.getFileUri());
+ fileEvent.setType(FileChangeType.Changed);
+ customization.getLanguageClient().notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
+
+ // Return the refreshed customization.
+ return refreshedCustomizationSupplier.get();
+ }
+
+ @SuppressWarnings("deprecation")
+ public static T replaceParameters(String newParameters,
+ EclipseCodeCustomization customization, Supplier refreshCustomizationSupplier) {
+ SymbolInformation symbol = customization.getSymbol();
+ Editor editor = customization.getEditor();
+ String fileName = customization.getFileName();
+ String fileUri = customization.getFileUri();
+ EclipseLanguageClient languageClient = customization.getLanguageClient();
+
+ // Beginning line of the symbol.
+ int line = symbol.getLocation().getRange().getStart().getLine();
+
+ // First find the starting location of the parameters.
+ // The beginning of the parameters may not be on the same line as the start of the signature.
+ Matcher matcher = BEGINNING_OF_PARAMETERS_PATTERN.matcher(editor.getFileLine(fileName, line));
+ while (!matcher.matches()) {
+ matcher = BEGINNING_OF_PARAMETERS_PATTERN.matcher(editor.getFileLine(fileName, ++line));
+ }
+
+ // Now that the line where the parameters begin is found create its position.
+ // Starting character is inclusive of the character offset, so add one as ')' isn't included in the capture.
+ Position parametersStart = new Position(line, matcher.group(1).length() + 1);
+
+ // Then find where the parameters end.
+ // The ending of the parameters may not be on the same line as the start of the parameters.
+ matcher = ENDING_OF_PARAMETERS_PATTERN.matcher(editor.getFileLine(fileName, line));
+ while (!matcher.matches()) {
+ matcher = ENDING_OF_PARAMETERS_PATTERN.matcher(editor.getFileLine(fileName, ++line));
+ }
+
+ // Now that the line where the parameters end is found gets create its position.
+ // Ending character is exclusive of the character offset.
+ Position parametersEnd = new Position(line, matcher.group(1).length());
+
+ editor.replace(fileName, parametersStart, parametersEnd, newParameters);
+
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(fileUri);
+ fileEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
+
+ return refreshCustomizationSupplier.get();
+ }
+
+ public static String getIndent(String content) {
+ Matcher matcher = INDENT_DETERMINATION_PATTERN.matcher(content);
+ return matcher.matches() ? matcher.group(1) : "";
+ }
+
+ /**
+ * Adds imports to the customization.
+ *
+ * @param importsToAdd Imports to add.
+ * @param customization Code customization to add imports.
+ * @param refreshCustomizationSupplier A supplier that returns a refreshed customization after the imports are
+ * added.
+ * @param Type of the customization.
+ * @return A refreshed customization.
+ */
+ public static T addImports(List importsToAdd,
+ EclipseClassCustomization customization, Supplier refreshCustomizationSupplier) {
+ EclipseLanguageClient languageClient = customization.getLanguageClient();
+ Editor editor = customization.getEditor();
+ String fileUri = customization.getFileUri();
+ String fileName = customization.getFileName();
+
+ // Only add imports if they exist.
+ if (!isNullOrEmpty(importsToAdd)) {
+ // Always place imports after the package.
+ // The language server will format the imports once added, so location doesn't matter.
+ int importLine = EclipseUtils.walkDownFileUntilLineMatches(editor, fileName, 0,
+ line -> PACKAGE_PATTERN.matcher(line).matches()) + 1;
+
+ Position importPosition = new Position(importLine, 0);
+ String imports = importsToAdd.stream()
+ .map(importToAdd -> "import " + importToAdd + ";")
+ .collect(Collectors.joining("\n"));
+
+ editor.insertBlankLine(fileName, importLine, false);
+ editor.replace(fileName, importPosition, importPosition, imports);
+ }
+
+ FileEvent fileEvent = new FileEvent();
+ fileEvent.setUri(fileUri);
+ fileEvent.setType(FileChangeType.Changed);
+ languageClient.notifyWatchedFilesChanged(Collections.singletonList(fileEvent));
+
+ return refreshCustomizationSupplier.get();
+ }
+
+ public static void organizeImportsOnRange(EclipseLanguageClient languageClient, Editor editor, String fileUri,
+ Range range) {
+ languageClient.listCodeActions(fileUri, range, CodeActionKind.SourceOrganizeImports)
+ .stream()
+ .filter(ca -> ca.getKind().equals(CodeActionKind.SourceOrganizeImports))
+ .findFirst()
+ .ifPresent(action -> EclipseUtils.applyWorkspaceEdit(action.getEdit(), editor, languageClient));
+ }
+
+ private EclipseUtils() {
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserClassCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserClassCustomization.java
new file mode 100644
index 00000000000..60589899b4e
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserClassCustomization.java
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.javaparsercustomization;
+
+import com.github.javaparser.StaticJavaParser;
+import com.github.javaparser.ast.CompilationUnit;
+import com.microsoft.typespec.http.client.generator.core.customization.ClassCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.ConstantCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.ConstructorCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.JavadocCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.MethodCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.PropertyCustomization;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * The class level customization for an AutoRest generated class.
+ */
+public final class JavaParserClassCustomization extends JavaParserCodeCustomization implements ClassCustomization {
+ private static final String UNSUPPORTED_MESSAGE
+ = "JavaParser-based ClassCustomization only supports the 'customizeAst' method.";
+ private final String className;
+
+ JavaParserClassCustomization(Editor editor, String packageName, String className) {
+ super(editor, packageName, className);
+
+ this.className = className;
+ }
+
+ @Override
+ public String getClassName() {
+ return className;
+ }
+
+ @Override
+ public JavaParserClassCustomization addImports(String... imports) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization addStaticBlock(String staticCodeBlock) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization addStaticBlock(String staticCodeBlock, List importsToAdd) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public MethodCustomization getMethod(String methodNameOrSignature) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public ConstructorCustomization getConstructor(String constructorNameOrSignature) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public PropertyCustomization getProperty(String propertyName) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public ConstantCustomization getConstant(String constantName) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavadocCustomization> getJavadoc() {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public ConstructorCustomization addConstructor(String constructor) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public ConstructorCustomization addConstructor(String constructor, List importsToAdd) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public MethodCustomization addMethod(String method) {
+ return addMethod(method, null);
+ }
+
+ @Override
+ public MethodCustomization addMethod(String method, List importsToAdd) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization removeMethod(String methodNameOrSignature) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization rename(String newName) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization setModifier(int modifiers) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization addAnnotation(String annotation) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization removeAnnotation(String annotation) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization renameEnumMember(String enumMemberName, String newName) {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+
+ @Override
+ public JavaParserClassCustomization customizeAst(Consumer astCustomization) {
+ CompilationUnit astToEdit = StaticJavaParser.parse(editor.getFileContent(fileName));
+ astCustomization.accept(astToEdit);
+ editor.replaceFile(fileName, astToEdit.toString());
+
+ return this;
+ }
+
+ public JavaParserClassCustomization refreshSymbol() {
+ throw new UnsupportedOperationException(UNSUPPORTED_MESSAGE);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserCodeCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserCodeCustomization.java
new file mode 100644
index 00000000000..85f159ef234
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserCodeCustomization.java
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.javaparsercustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.CodeCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.implementation.ls.EclipseLanguageClient;
+import org.eclipse.lsp4j.SymbolInformation;
+
+/**
+ * Base interface for all code based customizations.
+ */
+public abstract class JavaParserCodeCustomization implements CodeCustomization {
+ final Editor editor;
+ final String fileName;
+
+ JavaParserCodeCustomization(Editor editor, String packageName, String className) {
+ this.editor = editor;
+ this.fileName = "src/main/java/" + packageName.replace('.', '/') + "/" + className + ".java";
+ }
+
+ @Override
+ public final Editor getEditor() {
+ return editor;
+ }
+
+ @Override
+ public final EclipseLanguageClient getLanguageClient() {
+ return null;
+ }
+
+ @Override
+ public final SymbolInformation getSymbol() {
+ return null;
+ }
+
+ @Override
+ public final String getFileUri() {
+ return null;
+ }
+
+ @Override
+ public final String getFileName() {
+ return fileName;
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserLibraryCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserLibraryCustomization.java
new file mode 100644
index 00000000000..e471983a36f
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserLibraryCustomization.java
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.javaparsercustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.LibraryCustomization;
+
+/**
+ * Implementation of {@link LibraryCustomization} that uses the JavaParser library for customizations.
+ */
+public final class JavaParserLibraryCustomization implements LibraryCustomization {
+ private final Editor editor;
+
+ /**
+ * Constructor for JavaParserLibraryCustomization.
+ *
+ * @param editor The editor to use for customization.
+ */
+ public JavaParserLibraryCustomization(Editor editor) {
+ this.editor = editor;
+ }
+
+ @Override
+ public JavaParserPackageCustomization getPackage(String packageName) {
+ if (!editor.packageExists(packageName)) {
+ throw new IllegalArgumentException(packageName + " does not exist");
+ }
+ return new JavaParserPackageCustomization(editor, packageName);
+ }
+
+ @Override
+ public JavaParserClassCustomization getClass(String packageName, String className) {
+ return getPackage(packageName).getClass(className);
+ }
+
+ @Override
+ public Editor getRawEditor() {
+ return editor;
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserPackageCustomization.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserPackageCustomization.java
new file mode 100644
index 00000000000..4c92e5987bf
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/customization/implementation/javaparsercustomization/JavaParserPackageCustomization.java
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.customization.implementation.javaparsercustomization;
+
+import com.microsoft.typespec.http.client.generator.core.customization.ClassCustomization;
+import com.microsoft.typespec.http.client.generator.core.customization.Editor;
+import com.microsoft.typespec.http.client.generator.core.customization.PackageCustomization;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * The package level customization for an AutoRest generated client library.
+ */
+public final class JavaParserPackageCustomization implements PackageCustomization {
+ private final Editor editor;
+ private final String packageName;
+
+ JavaParserPackageCustomization(Editor editor, String packageName) {
+ this.editor = editor;
+ this.packageName = packageName;
+ }
+
+ @Override
+ public JavaParserClassCustomization getClass(String className) {
+ if (!editor.classExists(packageName, className)) {
+ throw new IllegalArgumentException(className + " does not exist in package " + packageName);
+ }
+
+ return new JavaParserClassCustomization(editor, packageName, className);
+ }
+
+ @Override
+ public List listClasses() {
+ return editor.classesInPackage(packageName)
+ .stream()
+ .map(className -> new JavaParserClassCustomization(editor, packageName, className))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/plugin/JavaSettings.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/plugin/JavaSettings.java
index e8ddfdf3673..c57220b15bf 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/plugin/JavaSettings.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/extension/plugin/JavaSettings.java
@@ -6,9 +6,11 @@
import com.azure.json.JsonProviders;
import com.azure.json.JsonReader;
import com.azure.json.JsonToken;
+import com.github.javaparser.JavaParser;
import com.microsoft.typespec.http.client.generator.core.mapper.Mappers;
import com.microsoft.typespec.http.client.generator.core.mapper.azurevnext.AzureVNextMapperFactory;
import com.microsoft.typespec.http.client.generator.core.mapper.clientcore.ClientCoreMapperFactory;
+import com.microsoft.typespec.http.client.generator.core.postprocessor.Postprocessor;
import com.microsoft.typespec.http.client.generator.core.template.Templates;
import com.microsoft.typespec.http.client.generator.core.template.azurevnext.AzureVNextTemplateFactory;
import com.microsoft.typespec.http.client.generator.core.template.clientcore.ClientCoreTemplateFactory;
@@ -386,6 +388,9 @@ private JavaSettings(AutorestSettings autorestSettings) {
// Whether to use object for unknown.
this.useObjectForUnknown = getBooleanValue(host, "use-object-for-unknown", false);
+
+ // Whether to use Eclipse Language Server when running code customizations.
+ this.useEclipseLanguageServer = getBooleanValue(host, "use-eclipse-language-server", true);
}
private void updateFlavorFactories() {
@@ -1505,6 +1510,24 @@ public boolean isUseObjectForUnknown() {
return useObjectForUnknown;
}
+ private final boolean useEclipseLanguageServer;
+
+ /**
+ * If there is {@link Postprocessor} code customizations to run, this determines whether to use the Eclipse Language
+ * Server to run the code customizations.
+ *
+ * This is a temporary setting until the Eclipse Language Server is removed and code customizations use
+ * {@link JavaParser} to run customizations. Switching to {@link JavaParser} will make customizations run faster but
+ * be more complicated to implement, where the latter part is okay as they're meant to be a "break the glass"
+ * situation where Swagger / TypeSpec definitions cannot convey what is required in code. Until this feature flag
+ * was added, code customizations were commonly used to gloss over issues with the Swagger / TypeSpec definitions.
+ *
+ * @return Whether to use the Eclipse Language Server to run the code customizations.
+ */
+ public boolean isUseEclipseLanguageServer() {
+ return useEclipseLanguageServer;
+ }
+
private static final String DEFAULT_CODE_GENERATION_HEADER
= String.join("\n", "Code generated by Microsoft (R) AutoRest Code Generator %s",
"Changes may cause incorrect behavior and will be lost if the code is regenerated.");
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/Postprocessor.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/Postprocessor.java
index 4aad12e272a..8339fc8129f 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/Postprocessor.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/Postprocessor.java
@@ -28,10 +28,12 @@
public class Postprocessor {
protected final NewPlugin plugin;
+ private final boolean useEclipseLanguageServer;
private final Logger logger;
- public Postprocessor(NewPlugin plugin) {
+ public Postprocessor(NewPlugin plugin, boolean useEclipseLanguageServer) {
this.plugin = plugin;
+ this.useEclipseLanguageServer = useEclipseLanguageServer;
this.logger = new PluginLogger(plugin, Postprocessor.class);
}
@@ -98,7 +100,7 @@ public void postProcess(Map fileContents) {
try {
Customization customization = customizationClass.getConstructor().newInstance();
logger.info("Running customization, this may take a while...");
- fileContents = customization.run(fileContents, logger);
+ fileContents = customization.run(fileContents, useEclipseLanguageServer, logger);
} catch (Exception e) {
logger.error("Unable to complete customization", e);
throw new RuntimeException("Unable to complete customization", e);
diff --git a/packages/http-client-java/generator/http-client-generator-test/Generate.ps1 b/packages/http-client-java/generator/http-client-generator-test/Generate.ps1
index 6013e462841..434656658fc 100644
--- a/packages/http-client-java/generator/http-client-generator-test/Generate.ps1
+++ b/packages/http-client-java/generator/http-client-generator-test/Generate.ps1
@@ -94,6 +94,13 @@ $generateScript = {
$tspOptions += " --option ""@typespec/http-client-java.customization-class=../../customization/src/main/java/CustomizationTest.java"""
}
+ # Test customization using only JavaParser for one of the TypeSpec definitions - naming-javaparser.tsp
+ if ($tspFile -match "tsp[\\/]naming-javaparser.tsp$") {
+ # Add the customization-class option for Java emitter
+ $tspOptions += " --option ""@typespec/http-client-java.customization-class=../../customization/src/main/java/JavaParserCustomizationTest.java"""
+ $tspOptions += " --option ""@typespec/http-client-java.use-eclipse-language-server=false"""
+ }
+
$tspTrace = "--trace import-resolution --trace projection --trace http-client-java"
$tspCommand = "npx --no-install tsp compile $tspFile $tspOptions $tspTrace"
@@ -126,62 +133,67 @@ $generateScript = {
}
}
-./Setup.ps1
+Push-Location $PSScriptRoot
+try {
+ ./Setup.ps1
-New-Item -Path ./existingcode/src/main/java/tsptest -ItemType Directory -Force | Out-Null
+ New-Item -Path ./existingcode/src/main/java/tsptest -ItemType Directory -Force | Out-Null
-if (Test-Path ./src/main/java/tsptest/partialupdate) {
- Copy-Item -Path ./src/main/java/tsptest/partialupdate -Destination ./existingcode/src/main/java/tsptest/partialupdate -Recurse -Force
-}
+ if (Test-Path ./src/main/java/tsptest/partialupdate) {
+ Copy-Item -Path ./src/main/java/tsptest/partialupdate -Destination ./existingcode/src/main/java/tsptest/partialupdate -Recurse -Force
+ }
-if (Test-Path ./src/main) {
- Remove-Item ./src/main -Recurse -Force
-}
-if (Test-Path ./src/samples) {
- Remove-Item ./src/samples -Recurse -Force
-}
-if (Test-Path ./src/test) {
- Get-ChildItem -Path ./src/test -Recurse -Directory | Where-Object {$_.Name -match "^generated$"} | Remove-Item -Recurse -Force
-}
-if (Test-Path ./tsp-output) {
- Remove-Item ./tsp-output -Recurse -Force
-}
+ if (Test-Path ./src/main) {
+ Remove-Item ./src/main -Recurse -Force
+ }
+ if (Test-Path ./src/samples) {
+ Remove-Item ./src/samples -Recurse -Force
+ }
+ if (Test-Path ./src/test) {
+ Get-ChildItem -Path ./src/test -Recurse -Directory | Where-Object {$_.Name -match "^generated$"} | Remove-Item -Recurse -Force
+ }
+ if (Test-Path ./tsp-output) {
+ Remove-Item ./tsp-output -Recurse -Force
+ }
-# generate for other local test sources except partial update
-$job = Get-Item ./tsp/* -Filter "*.tsp" -Exclude "*partialupdate*" | ForEach-Object -Parallel $generateScript -ThrottleLimit $Parallelization -AsJob
+ # generate for other local test sources except partial update
+ $job = Get-Item ./tsp/* -Filter "*.tsp" -Exclude "*partialupdate*" | ForEach-Object -Parallel $generateScript -ThrottleLimit $Parallelization -AsJob
-$job | Wait-Job -Timeout 600
-$job | Receive-Job
+ $job | Wait-Job -Timeout 600
+ $job | Receive-Job
-# partial update test
-npx --no-install tsp compile ./tsp/partialupdate.tsp --option="@typespec/http-client-java.emitter-output-dir={project-root}/existingcode"
-Copy-Item -Path ./existingcode/src/main/java/tsptest/partialupdate -Destination ./src/main/java/tsptest/partialupdate -Recurse -Force
-Remove-Item ./existingcode -Recurse -Force
+ # partial update test
+ npx --no-install tsp compile ./tsp/partialupdate.tsp --option="@typespec/http-client-java.emitter-output-dir={project-root}/existingcode"
+ Copy-Item -Path ./existingcode/src/main/java/tsptest/partialupdate -Destination ./src/main/java/tsptest/partialupdate -Recurse -Force
+ Remove-Item ./existingcode -Recurse -Force
-# generate for http-specs/azure-http-specs test sources
-Copy-Item -Path node_modules/@typespec/http-specs/specs -Destination ./ -Recurse -Force
-Copy-Item -Path node_modules/@azure-tools/azure-http-specs/specs -Destination ./ -Recurse -Force
-# remove xml tests, emitter has not supported xml model
-Remove-Item ./specs/payload/xml -Recurse -Force
+ # generate for http-specs/azure-http-specs test sources
+ Copy-Item -Path node_modules/@typespec/http-specs/specs -Destination ./ -Recurse -Force
+ Copy-Item -Path node_modules/@azure-tools/azure-http-specs/specs -Destination ./ -Recurse -Force
+ # remove xml tests, emitter has not supported xml model
+ Remove-Item ./specs/payload/xml -Recurse -Force
-$job = (Get-ChildItem ./specs -Include "main.tsp","old.tsp" -File -Recurse) | ForEach-Object -Parallel $generateScript -ThrottleLimit $Parallelization -AsJob
+ $job = (Get-ChildItem ./specs -Include "main.tsp","old.tsp" -File -Recurse) | ForEach-Object -Parallel $generateScript -ThrottleLimit $Parallelization -AsJob
-$job | Wait-Job -Timeout 1200
-$job | Receive-Job
+ $job | Wait-Job -Timeout 1200
+ $job | Receive-Job
-Remove-Item ./specs -Recurse -Force
+ Remove-Item ./specs -Recurse -Force
-Copy-Item -Path ./tsp-output/*/src -Destination ./ -Recurse -Force -Exclude @("ReadmeSamples.java", "module-info.java")
+ Copy-Item -Path ./tsp-output/*/src -Destination ./ -Recurse -Force -Exclude @("ReadmeSamples.java", "module-info.java")
-Remove-Item ./tsp-output -Recurse -Force
+ Remove-Item ./tsp-output -Recurse -Force
-if (Test-Path ./src/main/resources/META-INF/client-structure-service_metadata.json) {
- # client structure is generated from multiple client.tsp files and the last one to execute overwrites
- # the api view properties file. Because the tests run in parallel, the order is not guaranteed. This
- # causes git diff check to fail as the checked in file is not the same as the generated one.
- Remove-Item ./src/main/resources/META-INF/client-structure-service_metadata.json -Force
-}
+ if (Test-Path ./src/main/resources/META-INF/client-structure-service_metadata.json) {
+ # client structure is generated from multiple client.tsp files and the last one to execute overwrites
+ # the api view properties file. Because the tests run in parallel, the order is not guaranteed. This
+ # causes git diff check to fail as the checked in file is not the same as the generated one.
+ Remove-Item ./src/main/resources/META-INF/client-structure-service_metadata.json -Force
+ }
-if ($ExitCode -ne 0) {
- throw "Failed to generate from tsp"
+ if ($ExitCode -ne 0) {
+ throw "Failed to generate from tsp"
+ }
+} finally {
+ Pop-Location
}
diff --git a/packages/http-client-java/generator/http-client-generator-test/Setup.ps1 b/packages/http-client-java/generator/http-client-generator-test/Setup.ps1
index 71a0d59e6c4..97b4f96f39c 100644
--- a/packages/http-client-java/generator/http-client-generator-test/Setup.ps1
+++ b/packages/http-client-java/generator/http-client-generator-test/Setup.ps1
@@ -1,11 +1,31 @@
# re-build http-client-java
# hack to allow additionalProperties in this test
-(Get-Content -Path "../../emitter/src/options.ts") -replace "additionalProperties: false,", "additionalProperties: true," | Set-Content -Path "../../emitter/src/options.ts"
try {
- Set-Location (Resolve-Path (Join-Path $PSScriptRoot '..' '..'))
- ./Setup.ps1
- Set-Location $PSScriptRoot
- npm run clean && npm install
+ Push-Location $PSScriptRoot
+ try {
+ (Get-Content -Path "../../emitter/src/options.ts") -replace "additionalProperties: false,", "additionalProperties: true," | Set-Content -Path "../../emitter/src/options.ts"
+ } finally {
+ Pop-Location
+ }
+
+ Push-Location (Resolve-Path (Join-Path $PSScriptRoot '..' '..'))
+ try {
+ ./Setup.ps1
+ } finally {
+ Pop-Location
+ }
+
+ Push-Location $PSScriptRoot
+ try {
+ npm run clean && npm install
+ } finally {
+ Pop-Location
+ }
} finally {
- (Get-Content -Path "../../emitter/src/options.ts") -replace "additionalProperties: true,", "additionalProperties: false," | Set-Content -Path "../../emitter/src/options.ts"
+ Push-Location $PSScriptRoot
+ try {
+ (Get-Content -Path "../../emitter/src/options.ts") -replace "additionalProperties: true,", "additionalProperties: false," | Set-Content -Path "../../emitter/src/options.ts"
+ } finally {
+ Pop-Location
+ }
}
diff --git a/packages/http-client-java/generator/http-client-generator-test/customization/src/main/java/JavaParserCustomizationTest.java b/packages/http-client-java/generator/http-client-generator-test/customization/src/main/java/JavaParserCustomizationTest.java
new file mode 100644
index 00000000000..6babe2a3e36
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/customization/src/main/java/JavaParserCustomizationTest.java
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+import com.github.javaparser.javadoc.description.JavadocSnippet;
+import com.microsoft.typespec.http.client.generator.core.customization.Customization;
+import com.microsoft.typespec.http.client.generator.core.customization.LibraryCustomization;
+import org.slf4j.Logger;
+
+public class JavaParserCustomizationTest extends Customization {
+ @Override
+ public void customize(LibraryCustomization customization, Logger logger) {
+ logger.info("Customizing the NamingClient javadoc");
+ customization.getClass("tsptest.namingjavaparser", "NamingJavaParserClient")
+ .customizeAst(ast -> ast.getClassByName("NamingJavaParserClient")
+ .ifPresent(cu -> cu.getMethodsByName("postWithResponse")
+ .forEach(m -> m.getJavadoc().ifPresent(javadoc -> {
+ javadoc.getDescription().getElements().clear();
+ javadoc.getDescription().addElement(new JavadocSnippet("Protocol method for POST operation."));
+ }))));
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserAsyncClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserAsyncClient.java
new file mode 100644
index 00000000000..dd21304c508
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserAsyncClient.java
@@ -0,0 +1,209 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.ReturnType;
+import com.azure.core.annotation.ServiceClient;
+import com.azure.core.annotation.ServiceMethod;
+import com.azure.core.exception.ClientAuthenticationException;
+import com.azure.core.exception.HttpResponseException;
+import com.azure.core.exception.ResourceModifiedException;
+import com.azure.core.exception.ResourceNotFoundException;
+import com.azure.core.http.HttpHeaderName;
+import com.azure.core.http.rest.RequestOptions;
+import com.azure.core.http.rest.Response;
+import com.azure.core.util.BinaryData;
+import com.azure.core.util.FluxUtil;
+import reactor.core.publisher.Mono;
+import tsptest.namingjavaparser.implementation.NamingOpsImpl;
+import tsptest.namingjavaparser.models.DataRequest;
+import tsptest.namingjavaparser.models.DataResponse;
+import tsptest.namingjavaparser.models.GetAnonymousResponse;
+
+/**
+ * Initializes a new instance of the asynchronous NamingJavaParserClient type.
+ */
+@ServiceClient(builder = NamingJavaParserClientBuilder.class, isAsync = true)
+public final class NamingJavaParserAsyncClient {
+ @Generated
+ private final NamingOpsImpl serviceClient;
+
+ /**
+ * Initializes an instance of NamingJavaParserAsyncClient class.
+ *
+ * @param serviceClient the service client implementation.
+ */
+ @Generated
+ NamingJavaParserAsyncClient(NamingOpsImpl serviceClient) {
+ this.serviceClient = serviceClient;
+ }
+
+ /**
+ * summary of POST op
+ *
+ * description of POST op.
+ * Header Parameters
+ *
+ * Header Parameters
+ * | Name | Type | Required | Description |
+ * | etag | String | No | summary of etag header parameter
+ *
+ * description of etag header parameter |
+ *
+ * You can add these to a request with {@link RequestOptions#addHeader}
+ * Request Body Schema
+ *
+ *
+ * {@code
+ * {
+ * parameters (Optional): {
+ * type: String(Type1/Type2) (Required)
+ * }
+ * }
+ * }
+ *
+ *
+ * Response Body Schema
+ *
+ *
+ * {@code
+ * {
+ * name: String (Required)
+ * data (Required): {
+ * data (Required): {
+ * @data.kind: String (Required)
+ * }
+ * }
+ * type: String(Blob/File) (Required)
+ * status: String(Running/Completed/Failed) (Required)
+ * anonymous (Required): {
+ * last_error (Required): {
+ * code: String(server_error/rate_limit_exceeded/invalid_prompt) (Required)
+ * }
+ * }
+ * }
+ * }
+ *
+ *
+ * @param name summary of name query parameter
+ *
+ * description of name query parameter.
+ * @param body The body parameter.
+ * @param requestOptions The options to configure the HTTP request before HTTP client sends it.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @return summary of Response along with {@link Response} on successful completion of {@link Mono}.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Mono> postWithResponse(String name, BinaryData body, RequestOptions requestOptions) {
+ return this.serviceClient.postWithResponseAsync(name, body, requestOptions);
+ }
+
+ /**
+ * The getAnonymous operation.
+ * Response Body Schema
+ *
+ *
+ * {@code
+ * {
+ * name: String (Required)
+ * }
+ * }
+ *
+ *
+ * @param requestOptions The options to configure the HTTP request before HTTP client sends it.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @return the response body along with {@link Response} on successful completion of {@link Mono}.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Mono> getAnonymousWithResponse(RequestOptions requestOptions) {
+ return this.serviceClient.getAnonymousWithResponseAsync(requestOptions);
+ }
+
+ /**
+ * summary of POST op
+ *
+ * description of POST op.
+ *
+ * @param name summary of name query parameter
+ *
+ * description of name query parameter.
+ * @param body The body parameter.
+ * @param etag summary of etag header parameter
+ *
+ * description of etag header parameter.
+ * @throws IllegalArgumentException thrown if parameters fail the validation.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
+ * @return summary of Response on successful completion of {@link Mono}.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Mono post(String name, DataRequest body, String etag) {
+ // Generated convenience method for postWithResponse
+ RequestOptions requestOptions = new RequestOptions();
+ if (etag != null) {
+ requestOptions.setHeader(HttpHeaderName.ETAG, etag);
+ }
+ return postWithResponse(name, BinaryData.fromObject(body), requestOptions).flatMap(FluxUtil::toMono)
+ .map(protocolMethodData -> protocolMethodData.toObject(DataResponse.class));
+ }
+
+ /**
+ * summary of POST op
+ *
+ * description of POST op.
+ *
+ * @param name summary of name query parameter
+ *
+ * description of name query parameter.
+ * @param body The body parameter.
+ * @throws IllegalArgumentException thrown if parameters fail the validation.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
+ * @return summary of Response on successful completion of {@link Mono}.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Mono post(String name, DataRequest body) {
+ // Generated convenience method for postWithResponse
+ RequestOptions requestOptions = new RequestOptions();
+ return postWithResponse(name, BinaryData.fromObject(body), requestOptions).flatMap(FluxUtil::toMono)
+ .map(protocolMethodData -> protocolMethodData.toObject(DataResponse.class));
+ }
+
+ /**
+ * The getAnonymous operation.
+ *
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
+ * @return the response body on successful completion of {@link Mono}.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Mono getAnonymous() {
+ // Generated convenience method for getAnonymousWithResponse
+ RequestOptions requestOptions = new RequestOptions();
+ return getAnonymousWithResponse(requestOptions).flatMap(FluxUtil::toMono)
+ .map(protocolMethodData -> protocolMethodData.toObject(GetAnonymousResponse.class));
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserClient.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserClient.java
new file mode 100644
index 00000000000..a132fc27fb5
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserClient.java
@@ -0,0 +1,206 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+package tsptest.namingjavaparser;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.ReturnType;
+import com.azure.core.annotation.ServiceClient;
+import com.azure.core.annotation.ServiceMethod;
+import com.azure.core.exception.ClientAuthenticationException;
+import com.azure.core.exception.HttpResponseException;
+import com.azure.core.exception.ResourceModifiedException;
+import com.azure.core.exception.ResourceNotFoundException;
+import com.azure.core.http.HttpHeaderName;
+import com.azure.core.http.rest.RequestOptions;
+import com.azure.core.http.rest.Response;
+import com.azure.core.util.BinaryData;
+import tsptest.namingjavaparser.implementation.NamingOpsImpl;
+import tsptest.namingjavaparser.models.DataRequest;
+import tsptest.namingjavaparser.models.DataResponse;
+import tsptest.namingjavaparser.models.GetAnonymousResponse;
+
+/**
+ * Initializes a new instance of the synchronous NamingJavaParserClient type.
+ */
+@ServiceClient(builder = NamingJavaParserClientBuilder.class)
+public final class NamingJavaParserClient {
+
+ @Generated
+ private final NamingOpsImpl serviceClient;
+
+ /**
+ * Initializes an instance of NamingJavaParserClient class.
+ *
+ * @param serviceClient the service client implementation.
+ */
+ @Generated
+ NamingJavaParserClient(NamingOpsImpl serviceClient) {
+ this.serviceClient = serviceClient;
+ }
+
+ /**
+ * summary of POST op
+ *
+ * description of POST op.
+ * Header Parameters
+ *
+ * Header Parameters
+ * | Name | Type | Required | Description |
+ * | etag | String | No | summary of etag header parameter
+ *
+ * description of etag header parameter |
+ *
+ * You can add these to a request with {@link RequestOptions#addHeader}
+ * Request Body Schema
+ *
+ *
+ * {@code
+ * {
+ * parameters (Optional): {
+ * type: String(Type1/Type2) (Required)
+ * }
+ * }
+ * }
+ *
+ *
+ * Response Body Schema
+ *
+ *
+ * {@code
+ * {
+ * name: String (Required)
+ * data (Required): {
+ * data (Required): {
+ * @data.kind: String (Required)
+ * }
+ * }
+ * type: String(Blob/File) (Required)
+ * status: String(Running/Completed/Failed) (Required)
+ * anonymous (Required): {
+ * last_error (Required): {
+ * code: String(server_error/rate_limit_exceeded/invalid_prompt) (Required)
+ * }
+ * }
+ * }
+ * }
+ *
+ *
+ * @param name summary of name query parameter
+ *
+ * description of name query parameter.
+ * @param body The body parameter.
+ * @param requestOptions The options to configure the HTTP request before HTTP client sends it.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @return summary of Response along with {@link Response}.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Response postWithResponse(String name, BinaryData body, RequestOptions requestOptions) {
+ return this.serviceClient.postWithResponse(name, body, requestOptions);
+ }
+
+ /**
+ * The getAnonymous operation.
+ * Response Body Schema
+ *
+ *
+ * {@code
+ * {
+ * name: String (Required)
+ * }
+ * }
+ *
+ *
+ * @param requestOptions The options to configure the HTTP request before HTTP client sends it.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @return the response body along with {@link Response}.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Response getAnonymousWithResponse(RequestOptions requestOptions) {
+ return this.serviceClient.getAnonymousWithResponse(requestOptions);
+ }
+
+ /**
+ * summary of POST op
+ *
+ * description of POST op.
+ *
+ * @param name summary of name query parameter
+ *
+ * description of name query parameter.
+ * @param body The body parameter.
+ * @param etag summary of etag header parameter
+ *
+ * description of etag header parameter.
+ * @throws IllegalArgumentException thrown if parameters fail the validation.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
+ * @return summary of Response.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public DataResponse post(String name, DataRequest body, String etag) {
+ // Generated convenience method for postWithResponse
+ RequestOptions requestOptions = new RequestOptions();
+ if (etag != null) {
+ requestOptions.setHeader(HttpHeaderName.ETAG, etag);
+ }
+ return postWithResponse(name, BinaryData.fromObject(body), requestOptions).getValue()
+ .toObject(DataResponse.class);
+ }
+
+ /**
+ * summary of POST op
+ *
+ * description of POST op.
+ *
+ * @param name summary of name query parameter
+ *
+ * description of name query parameter.
+ * @param body The body parameter.
+ * @throws IllegalArgumentException thrown if parameters fail the validation.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
+ * @return summary of Response.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public DataResponse post(String name, DataRequest body) {
+ // Generated convenience method for postWithResponse
+ RequestOptions requestOptions = new RequestOptions();
+ return postWithResponse(name, BinaryData.fromObject(body), requestOptions).getValue()
+ .toObject(DataResponse.class);
+ }
+
+ /**
+ * The getAnonymous operation.
+ *
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
+ * @return the response.
+ */
+ @Generated
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public GetAnonymousResponse getAnonymous() {
+ // Generated convenience method for getAnonymousWithResponse
+ RequestOptions requestOptions = new RequestOptions();
+ return getAnonymousWithResponse(requestOptions).getValue().toObject(GetAnonymousResponse.class);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserClientBuilder.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserClientBuilder.java
new file mode 100644
index 00000000000..9a555ac2b0e
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/NamingJavaParserClientBuilder.java
@@ -0,0 +1,288 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.ServiceClientBuilder;
+import com.azure.core.client.traits.ConfigurationTrait;
+import com.azure.core.client.traits.EndpointTrait;
+import com.azure.core.client.traits.HttpTrait;
+import com.azure.core.http.HttpClient;
+import com.azure.core.http.HttpHeaders;
+import com.azure.core.http.HttpPipeline;
+import com.azure.core.http.HttpPipelineBuilder;
+import com.azure.core.http.HttpPipelinePosition;
+import com.azure.core.http.policy.AddDatePolicy;
+import com.azure.core.http.policy.AddHeadersFromContextPolicy;
+import com.azure.core.http.policy.AddHeadersPolicy;
+import com.azure.core.http.policy.HttpLogOptions;
+import com.azure.core.http.policy.HttpLoggingPolicy;
+import com.azure.core.http.policy.HttpPipelinePolicy;
+import com.azure.core.http.policy.HttpPolicyProviders;
+import com.azure.core.http.policy.RequestIdPolicy;
+import com.azure.core.http.policy.RetryOptions;
+import com.azure.core.http.policy.RetryPolicy;
+import com.azure.core.http.policy.UserAgentPolicy;
+import com.azure.core.util.ClientOptions;
+import com.azure.core.util.Configuration;
+import com.azure.core.util.CoreUtils;
+import com.azure.core.util.builder.ClientBuilderUtil;
+import com.azure.core.util.logging.ClientLogger;
+import com.azure.core.util.serializer.JacksonAdapter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import tsptest.namingjavaparser.implementation.NamingJavaParserClientImpl;
+
+/**
+ * A builder for creating a new instance of the NamingJavaParserClient type.
+ */
+@ServiceClientBuilder(serviceClients = { NamingJavaParserClient.class, NamingJavaParserAsyncClient.class })
+public final class NamingJavaParserClientBuilder implements HttpTrait,
+ ConfigurationTrait, EndpointTrait {
+ @Generated
+ private static final String SDK_NAME = "name";
+
+ @Generated
+ private static final String SDK_VERSION = "version";
+
+ @Generated
+ private static final Map PROPERTIES
+ = CoreUtils.getProperties("tsptest-namingjavaparser.properties");
+
+ @Generated
+ private final List pipelinePolicies;
+
+ /**
+ * Create an instance of the NamingJavaParserClientBuilder.
+ */
+ @Generated
+ public NamingJavaParserClientBuilder() {
+ this.pipelinePolicies = new ArrayList<>();
+ }
+
+ /*
+ * The HTTP client used to send the request.
+ */
+ @Generated
+ private HttpClient httpClient;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Generated
+ @Override
+ public NamingJavaParserClientBuilder httpClient(HttpClient httpClient) {
+ this.httpClient = httpClient;
+ return this;
+ }
+
+ /*
+ * The HTTP pipeline to send requests through.
+ */
+ @Generated
+ private HttpPipeline pipeline;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Generated
+ @Override
+ public NamingJavaParserClientBuilder pipeline(HttpPipeline pipeline) {
+ if (this.pipeline != null && pipeline == null) {
+ LOGGER.atInfo().log("HttpPipeline is being set to 'null' when it was previously configured.");
+ }
+ this.pipeline = pipeline;
+ return this;
+ }
+
+ /*
+ * The logging configuration for HTTP requests and responses.
+ */
+ @Generated
+ private HttpLogOptions httpLogOptions;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Generated
+ @Override
+ public NamingJavaParserClientBuilder httpLogOptions(HttpLogOptions httpLogOptions) {
+ this.httpLogOptions = httpLogOptions;
+ return this;
+ }
+
+ /*
+ * The client options such as application ID and custom headers to set on a request.
+ */
+ @Generated
+ private ClientOptions clientOptions;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Generated
+ @Override
+ public NamingJavaParserClientBuilder clientOptions(ClientOptions clientOptions) {
+ this.clientOptions = clientOptions;
+ return this;
+ }
+
+ /*
+ * The retry options to configure retry policy for failed requests.
+ */
+ @Generated
+ private RetryOptions retryOptions;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Generated
+ @Override
+ public NamingJavaParserClientBuilder retryOptions(RetryOptions retryOptions) {
+ this.retryOptions = retryOptions;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Generated
+ @Override
+ public NamingJavaParserClientBuilder addPolicy(HttpPipelinePolicy customPolicy) {
+ Objects.requireNonNull(customPolicy, "'customPolicy' cannot be null.");
+ pipelinePolicies.add(customPolicy);
+ return this;
+ }
+
+ /*
+ * The configuration store that is used during construction of the service client.
+ */
+ @Generated
+ private Configuration configuration;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Generated
+ @Override
+ public NamingJavaParserClientBuilder configuration(Configuration configuration) {
+ this.configuration = configuration;
+ return this;
+ }
+
+ /*
+ * The service endpoint
+ */
+ @Generated
+ private String endpoint;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Generated
+ @Override
+ public NamingJavaParserClientBuilder endpoint(String endpoint) {
+ this.endpoint = endpoint;
+ return this;
+ }
+
+ /*
+ * The retry policy that will attempt to retry failed requests, if applicable.
+ */
+ @Generated
+ private RetryPolicy retryPolicy;
+
+ /**
+ * Sets The retry policy that will attempt to retry failed requests, if applicable.
+ *
+ * @param retryPolicy the retryPolicy value.
+ * @return the NamingJavaParserClientBuilder.
+ */
+ @Generated
+ public NamingJavaParserClientBuilder retryPolicy(RetryPolicy retryPolicy) {
+ this.retryPolicy = retryPolicy;
+ return this;
+ }
+
+ /**
+ * Builds an instance of NamingJavaParserClientImpl with the provided parameters.
+ *
+ * @return an instance of NamingJavaParserClientImpl.
+ */
+ @Generated
+ private NamingJavaParserClientImpl buildInnerClient() {
+ this.validateClient();
+ HttpPipeline localPipeline = (pipeline != null) ? pipeline : createHttpPipeline();
+ NamingJavaParserClientImpl client = new NamingJavaParserClientImpl(localPipeline,
+ JacksonAdapter.createDefaultSerializerAdapter(), this.endpoint);
+ return client;
+ }
+
+ @Generated
+ private void validateClient() {
+ // This method is invoked from 'buildInnerClient'/'buildClient' method.
+ // Developer can customize this method, to validate that the necessary conditions are met for the new client.
+ Objects.requireNonNull(endpoint, "'endpoint' cannot be null.");
+ }
+
+ @Generated
+ private HttpPipeline createHttpPipeline() {
+ Configuration buildConfiguration
+ = (configuration == null) ? Configuration.getGlobalConfiguration() : configuration;
+ HttpLogOptions localHttpLogOptions = this.httpLogOptions == null ? new HttpLogOptions() : this.httpLogOptions;
+ ClientOptions localClientOptions = this.clientOptions == null ? new ClientOptions() : this.clientOptions;
+ List policies = new ArrayList<>();
+ String clientName = PROPERTIES.getOrDefault(SDK_NAME, "UnknownName");
+ String clientVersion = PROPERTIES.getOrDefault(SDK_VERSION, "UnknownVersion");
+ String applicationId = CoreUtils.getApplicationId(localClientOptions, localHttpLogOptions);
+ policies.add(new UserAgentPolicy(applicationId, clientName, clientVersion, buildConfiguration));
+ policies.add(new RequestIdPolicy());
+ policies.add(new AddHeadersFromContextPolicy());
+ HttpHeaders headers = CoreUtils.createHttpHeadersFromClientOptions(localClientOptions);
+ if (headers != null) {
+ policies.add(new AddHeadersPolicy(headers));
+ }
+ this.pipelinePolicies.stream()
+ .filter(p -> p.getPipelinePosition() == HttpPipelinePosition.PER_CALL)
+ .forEach(p -> policies.add(p));
+ HttpPolicyProviders.addBeforeRetryPolicies(policies);
+ policies.add(ClientBuilderUtil.validateAndGetRetryPolicy(retryPolicy, retryOptions, new RetryPolicy()));
+ policies.add(new AddDatePolicy());
+ this.pipelinePolicies.stream()
+ .filter(p -> p.getPipelinePosition() == HttpPipelinePosition.PER_RETRY)
+ .forEach(p -> policies.add(p));
+ HttpPolicyProviders.addAfterRetryPolicies(policies);
+ policies.add(new HttpLoggingPolicy(localHttpLogOptions));
+ HttpPipeline httpPipeline = new HttpPipelineBuilder().policies(policies.toArray(new HttpPipelinePolicy[0]))
+ .httpClient(httpClient)
+ .clientOptions(localClientOptions)
+ .build();
+ return httpPipeline;
+ }
+
+ /**
+ * Builds an instance of NamingJavaParserAsyncClient class.
+ *
+ * @return an instance of NamingJavaParserAsyncClient.
+ */
+ @Generated
+ public NamingJavaParserAsyncClient buildAsyncClient() {
+ return new NamingJavaParserAsyncClient(buildInnerClient().getNamingOps());
+ }
+
+ /**
+ * Builds an instance of NamingJavaParserClient class.
+ *
+ * @return an instance of NamingJavaParserClient.
+ */
+ @Generated
+ public NamingJavaParserClient buildClient() {
+ return new NamingJavaParserClient(buildInnerClient().getNamingOps());
+ }
+
+ private static final ClientLogger LOGGER = new ClientLogger(NamingJavaParserClientBuilder.class);
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/NamingJavaParserClientImpl.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/NamingJavaParserClientImpl.java
new file mode 100644
index 00000000000..c159ed60d4b
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/NamingJavaParserClientImpl.java
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.implementation;
+
+import com.azure.core.http.HttpPipeline;
+import com.azure.core.http.HttpPipelineBuilder;
+import com.azure.core.http.policy.RetryPolicy;
+import com.azure.core.http.policy.UserAgentPolicy;
+import com.azure.core.util.serializer.JacksonAdapter;
+import com.azure.core.util.serializer.SerializerAdapter;
+
+/**
+ * Initializes a new instance of the NamingJavaParserClient type.
+ */
+public final class NamingJavaParserClientImpl {
+ /**
+ * Service host.
+ */
+ private final String endpoint;
+
+ /**
+ * Gets Service host.
+ *
+ * @return the endpoint value.
+ */
+ public String getEndpoint() {
+ return this.endpoint;
+ }
+
+ /**
+ * The HTTP pipeline to send requests through.
+ */
+ private final HttpPipeline httpPipeline;
+
+ /**
+ * Gets The HTTP pipeline to send requests through.
+ *
+ * @return the httpPipeline value.
+ */
+ public HttpPipeline getHttpPipeline() {
+ return this.httpPipeline;
+ }
+
+ /**
+ * The serializer to serialize an object into a string.
+ */
+ private final SerializerAdapter serializerAdapter;
+
+ /**
+ * Gets The serializer to serialize an object into a string.
+ *
+ * @return the serializerAdapter value.
+ */
+ public SerializerAdapter getSerializerAdapter() {
+ return this.serializerAdapter;
+ }
+
+ /**
+ * The NamingOpsImpl object to access its operations.
+ */
+ private final NamingOpsImpl namingOps;
+
+ /**
+ * Gets the NamingOpsImpl object to access its operations.
+ *
+ * @return the NamingOpsImpl object.
+ */
+ public NamingOpsImpl getNamingOps() {
+ return this.namingOps;
+ }
+
+ /**
+ * Initializes an instance of NamingJavaParserClient client.
+ *
+ * @param endpoint Service host.
+ */
+ public NamingJavaParserClientImpl(String endpoint) {
+ this(new HttpPipelineBuilder().policies(new UserAgentPolicy(), new RetryPolicy()).build(),
+ JacksonAdapter.createDefaultSerializerAdapter(), endpoint);
+ }
+
+ /**
+ * Initializes an instance of NamingJavaParserClient client.
+ *
+ * @param httpPipeline The HTTP pipeline to send requests through.
+ * @param endpoint Service host.
+ */
+ public NamingJavaParserClientImpl(HttpPipeline httpPipeline, String endpoint) {
+ this(httpPipeline, JacksonAdapter.createDefaultSerializerAdapter(), endpoint);
+ }
+
+ /**
+ * Initializes an instance of NamingJavaParserClient client.
+ *
+ * @param httpPipeline The HTTP pipeline to send requests through.
+ * @param serializerAdapter The serializer to serialize an object into a string.
+ * @param endpoint Service host.
+ */
+ public NamingJavaParserClientImpl(HttpPipeline httpPipeline, SerializerAdapter serializerAdapter, String endpoint) {
+ this.httpPipeline = httpPipeline;
+ this.serializerAdapter = serializerAdapter;
+ this.endpoint = endpoint;
+ this.namingOps = new NamingOpsImpl(this);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/NamingOpsImpl.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/NamingOpsImpl.java
new file mode 100644
index 00000000000..eda7bd97753
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/NamingOpsImpl.java
@@ -0,0 +1,285 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.implementation;
+
+import com.azure.core.annotation.BodyParam;
+import com.azure.core.annotation.ExpectedResponses;
+import com.azure.core.annotation.Get;
+import com.azure.core.annotation.HeaderParam;
+import com.azure.core.annotation.Host;
+import com.azure.core.annotation.HostParam;
+import com.azure.core.annotation.Post;
+import com.azure.core.annotation.QueryParam;
+import com.azure.core.annotation.ReturnType;
+import com.azure.core.annotation.ServiceInterface;
+import com.azure.core.annotation.ServiceMethod;
+import com.azure.core.annotation.UnexpectedResponseExceptionType;
+import com.azure.core.exception.ClientAuthenticationException;
+import com.azure.core.exception.HttpResponseException;
+import com.azure.core.exception.ResourceModifiedException;
+import com.azure.core.exception.ResourceNotFoundException;
+import com.azure.core.http.rest.RequestOptions;
+import com.azure.core.http.rest.Response;
+import com.azure.core.http.rest.RestProxy;
+import com.azure.core.util.BinaryData;
+import com.azure.core.util.Context;
+import com.azure.core.util.FluxUtil;
+import reactor.core.publisher.Mono;
+
+/**
+ * An instance of this class provides access to all the operations defined in NamingOps.
+ */
+public final class NamingOpsImpl {
+ /**
+ * The proxy service used to perform REST calls.
+ */
+ private final NamingOpsService service;
+
+ /**
+ * The service client containing this operation class.
+ */
+ private final NamingJavaParserClientImpl client;
+
+ /**
+ * Initializes an instance of NamingOpsImpl.
+ *
+ * @param client the instance of the service client containing this operation class.
+ */
+ NamingOpsImpl(NamingJavaParserClientImpl client) {
+ this.service
+ = RestProxy.create(NamingOpsService.class, client.getHttpPipeline(), client.getSerializerAdapter());
+ this.client = client;
+ }
+
+ /**
+ * The interface defining all the services for NamingJavaParserClientNamingOps to be used by the proxy service to
+ * perform REST calls.
+ */
+ @Host("{endpoint}")
+ @ServiceInterface(name = "NamingJavaParserClie")
+ public interface NamingOpsService {
+ @Post("/naming")
+ @ExpectedResponses({ 200 })
+ @UnexpectedResponseExceptionType(value = ClientAuthenticationException.class, code = { 401 })
+ @UnexpectedResponseExceptionType(value = ResourceNotFoundException.class, code = { 404 })
+ @UnexpectedResponseExceptionType(value = ResourceModifiedException.class, code = { 409 })
+ @UnexpectedResponseExceptionType(HttpResponseException.class)
+ Mono> post(@HostParam("endpoint") String endpoint, @QueryParam("name") String name,
+ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept,
+ @BodyParam("application/json") BinaryData body, RequestOptions requestOptions, Context context);
+
+ @Post("/naming")
+ @ExpectedResponses({ 200 })
+ @UnexpectedResponseExceptionType(value = ClientAuthenticationException.class, code = { 401 })
+ @UnexpectedResponseExceptionType(value = ResourceNotFoundException.class, code = { 404 })
+ @UnexpectedResponseExceptionType(value = ResourceModifiedException.class, code = { 409 })
+ @UnexpectedResponseExceptionType(HttpResponseException.class)
+ Response postSync(@HostParam("endpoint") String endpoint, @QueryParam("name") String name,
+ @HeaderParam("Content-Type") String contentType, @HeaderParam("Accept") String accept,
+ @BodyParam("application/json") BinaryData body, RequestOptions requestOptions, Context context);
+
+ @Get("/naming")
+ @ExpectedResponses({ 200 })
+ @UnexpectedResponseExceptionType(value = ClientAuthenticationException.class, code = { 401 })
+ @UnexpectedResponseExceptionType(value = ResourceNotFoundException.class, code = { 404 })
+ @UnexpectedResponseExceptionType(value = ResourceModifiedException.class, code = { 409 })
+ @UnexpectedResponseExceptionType(HttpResponseException.class)
+ Mono> getAnonymous(@HostParam("endpoint") String endpoint,
+ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context);
+
+ @Get("/naming")
+ @ExpectedResponses({ 200 })
+ @UnexpectedResponseExceptionType(value = ClientAuthenticationException.class, code = { 401 })
+ @UnexpectedResponseExceptionType(value = ResourceNotFoundException.class, code = { 404 })
+ @UnexpectedResponseExceptionType(value = ResourceModifiedException.class, code = { 409 })
+ @UnexpectedResponseExceptionType(HttpResponseException.class)
+ Response getAnonymousSync(@HostParam("endpoint") String endpoint,
+ @HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context);
+ }
+
+ /**
+ * summary of POST op
+ *
+ * description of POST op.
+ * Header Parameters
+ *
+ * Header Parameters
+ * | Name | Type | Required | Description |
+ * | etag | String | No | summary of etag header parameter
+ *
+ * description of etag header parameter |
+ *
+ * You can add these to a request with {@link RequestOptions#addHeader}
+ * Request Body Schema
+ *
+ *
+ * {@code
+ * {
+ * parameters (Optional): {
+ * type: String(Type1/Type2) (Required)
+ * }
+ * }
+ * }
+ *
+ *
+ * Response Body Schema
+ *
+ *
+ * {@code
+ * {
+ * name: String (Required)
+ * data (Required): {
+ * data (Required): {
+ * @data.kind: String (Required)
+ * }
+ * }
+ * type: String(Blob/File) (Required)
+ * status: String(Running/Completed/Failed) (Required)
+ * anonymous (Required): {
+ * last_error (Required): {
+ * code: String(server_error/rate_limit_exceeded/invalid_prompt) (Required)
+ * }
+ * }
+ * }
+ * }
+ *
+ *
+ * @param name summary of name query parameter
+ *
+ * description of name query parameter.
+ * @param body The body parameter.
+ * @param requestOptions The options to configure the HTTP request before HTTP client sends it.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @return summary of Response along with {@link Response} on successful completion of {@link Mono}.
+ */
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Mono> postWithResponseAsync(String name, BinaryData body,
+ RequestOptions requestOptions) {
+ final String contentType = "application/json";
+ final String accept = "application/json";
+ return FluxUtil.withContext(context -> service.post(this.client.getEndpoint(), name, contentType, accept, body,
+ requestOptions, context));
+ }
+
+ /**
+ * summary of POST op
+ *
+ * description of POST op.
+ * Header Parameters
+ *
+ * Header Parameters
+ * | Name | Type | Required | Description |
+ * | etag | String | No | summary of etag header parameter
+ *
+ * description of etag header parameter |
+ *
+ * You can add these to a request with {@link RequestOptions#addHeader}
+ * Request Body Schema
+ *
+ *
+ * {@code
+ * {
+ * parameters (Optional): {
+ * type: String(Type1/Type2) (Required)
+ * }
+ * }
+ * }
+ *
+ *
+ * Response Body Schema
+ *
+ *
+ * {@code
+ * {
+ * name: String (Required)
+ * data (Required): {
+ * data (Required): {
+ * @data.kind: String (Required)
+ * }
+ * }
+ * type: String(Blob/File) (Required)
+ * status: String(Running/Completed/Failed) (Required)
+ * anonymous (Required): {
+ * last_error (Required): {
+ * code: String(server_error/rate_limit_exceeded/invalid_prompt) (Required)
+ * }
+ * }
+ * }
+ * }
+ *
+ *
+ * @param name summary of name query parameter
+ *
+ * description of name query parameter.
+ * @param body The body parameter.
+ * @param requestOptions The options to configure the HTTP request before HTTP client sends it.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @return summary of Response along with {@link Response}.
+ */
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Response postWithResponse(String name, BinaryData body, RequestOptions requestOptions) {
+ final String contentType = "application/json";
+ final String accept = "application/json";
+ return service.postSync(this.client.getEndpoint(), name, contentType, accept, body, requestOptions,
+ Context.NONE);
+ }
+
+ /**
+ * The getAnonymous operation.
+ * Response Body Schema
+ *
+ *
+ * {@code
+ * {
+ * name: String (Required)
+ * }
+ * }
+ *
+ *
+ * @param requestOptions The options to configure the HTTP request before HTTP client sends it.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @return the response body along with {@link Response} on successful completion of {@link Mono}.
+ */
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Mono> getAnonymousWithResponseAsync(RequestOptions requestOptions) {
+ final String accept = "application/json";
+ return FluxUtil
+ .withContext(context -> service.getAnonymous(this.client.getEndpoint(), accept, requestOptions, context));
+ }
+
+ /**
+ * The getAnonymous operation.
+ * Response Body Schema
+ *
+ *
+ * {@code
+ * {
+ * name: String (Required)
+ * }
+ * }
+ *
+ *
+ * @param requestOptions The options to configure the HTTP request before HTTP client sends it.
+ * @throws HttpResponseException thrown if the request is rejected by server.
+ * @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
+ * @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
+ * @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
+ * @return the response body along with {@link Response}.
+ */
+ @ServiceMethod(returns = ReturnType.SINGLE)
+ public Response getAnonymousWithResponse(RequestOptions requestOptions) {
+ final String accept = "application/json";
+ return service.getAnonymousSync(this.client.getEndpoint(), accept, requestOptions, Context.NONE);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/package-info.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/package-info.java
new file mode 100644
index 00000000000..e91928ac172
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/implementation/package-info.java
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+/**
+ *
+ * Package containing the implementations for NamingJavaParser.
+ * description of Naming JavaParser.
+ *
+ */
+package tsptest.namingjavaparser.implementation;
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/BinaryData.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/BinaryData.java
new file mode 100644
index 00000000000..25ae34fe843
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/BinaryData.java
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.Immutable;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonSerializable;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * summary of Data
+ *
+ * description of Data.
+ */
+@Immutable
+public final class BinaryData implements JsonSerializable {
+ /*
+ * summary of data property
+ *
+ * description of data property
+ */
+ @Generated
+ private final Data data;
+
+ /**
+ * Creates an instance of BinaryData class.
+ *
+ * @param data the data value to set.
+ */
+ @Generated
+ private BinaryData(Data data) {
+ this.data = data;
+ }
+
+ /**
+ * Get the data property: summary of data property
+ *
+ * description of data property.
+ *
+ * @return the data value.
+ */
+ @Generated
+ public Data getData() {
+ return this.data;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeJsonField("data", this.data);
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of BinaryData from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of BinaryData if the JsonReader was pointing to an instance of it, or null if it was pointing
+ * to JSON null.
+ * @throws IllegalStateException If the deserialized JSON object was missing any required properties.
+ * @throws IOException If an error occurs while reading the BinaryData.
+ */
+ @Generated
+ public static BinaryData fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ Data data = null;
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("data".equals(fieldName)) {
+ data = Data.fromJson(reader);
+ } else {
+ reader.skipChildren();
+ }
+ }
+ return new BinaryData(data);
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/BytesData.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/BytesData.java
new file mode 100644
index 00000000000..b9bae240922
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/BytesData.java
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.Immutable;
+import com.azure.core.util.CoreUtils;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * The BytesData model.
+ */
+@Immutable
+public final class BytesData extends Data {
+ /*
+ * The @data.kind property.
+ */
+ @Generated
+ private String type = "bytes";
+
+ /*
+ * Data as {@code byte[]}
+ */
+ @Generated
+ private final byte[] dataAsBytes;
+
+ /**
+ * Creates an instance of BytesData class.
+ *
+ * @param dataAsBytes the dataAsBytes value to set.
+ */
+ @Generated
+ private BytesData(byte[] dataAsBytes) {
+ this.dataAsBytes = dataAsBytes;
+ }
+
+ /**
+ * Get the type property: The @data.kind property.
+ *
+ * @return the type value.
+ */
+ @Generated
+ @Override
+ public String getType() {
+ return this.type;
+ }
+
+ /**
+ * Get the dataAsBytes property: Data as {@code byte[]}.
+ *
+ * @return the dataAsBytes value.
+ */
+ @Generated
+ public byte[] getDataAsBytes() {
+ return CoreUtils.clone(this.dataAsBytes);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeBinaryField("data_bytes", this.dataAsBytes);
+ jsonWriter.writeStringField("@data.kind", this.type);
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of BytesData from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of BytesData if the JsonReader was pointing to an instance of it, or null if it was pointing
+ * to JSON null.
+ * @throws IllegalStateException If the deserialized JSON object was missing any required properties.
+ * @throws IOException If an error occurs while reading the BytesData.
+ */
+ @Generated
+ public static BytesData fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ byte[] dataAsBytes = null;
+ String type = "bytes";
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("data_bytes".equals(fieldName)) {
+ dataAsBytes = reader.getBinary();
+ } else if ("@data.kind".equals(fieldName)) {
+ type = reader.getString();
+ } else {
+ reader.skipChildren();
+ }
+ }
+ BytesData deserializedBytesData = new BytesData(dataAsBytes);
+ deserializedBytesData.type = type;
+
+ return deserializedBytesData;
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/Data.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/Data.java
new file mode 100644
index 00000000000..48caff332d5
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/Data.java
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.Immutable;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonSerializable;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * Dummy doc to make the javadoc break at the 'at' symbol. The type of the Data depends on
+ * @data.kind.letusmakeitlongsoitwouldbreakbeforethis field.
+ */
+@Immutable
+public class Data implements JsonSerializable {
+ /*
+ * The @data.kind property.
+ */
+ @Generated
+ private String type = "Data";
+
+ /**
+ * Creates an instance of Data class.
+ */
+ @Generated
+ protected Data() {
+ }
+
+ /**
+ * Get the type property: The @data.kind property.
+ *
+ * @return the type value.
+ */
+ @Generated
+ public String getType() {
+ return this.type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeStringField("@data.kind", this.type);
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of Data from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of Data if the JsonReader was pointing to an instance of it, or null if it was pointing to
+ * JSON null.
+ * @throws IOException If an error occurs while reading the Data.
+ */
+ @Generated
+ public static Data fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ String discriminatorValue = null;
+ try (JsonReader readerToUse = reader.bufferObject()) {
+ readerToUse.nextToken(); // Prepare for reading
+ while (readerToUse.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = readerToUse.getFieldName();
+ readerToUse.nextToken();
+ if ("@data.kind".equals(fieldName)) {
+ discriminatorValue = readerToUse.getString();
+ break;
+ } else {
+ readerToUse.skipChildren();
+ }
+ }
+ // Use the discriminator value to determine which subtype should be deserialized.
+ if ("bytes".equals(discriminatorValue)) {
+ return BytesData.fromJson(readerToUse.reset());
+ } else {
+ return fromJsonKnownDiscriminator(readerToUse.reset());
+ }
+ }
+ });
+ }
+
+ @Generated
+ static Data fromJsonKnownDiscriminator(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ Data deserializedData = new Data();
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("@data.kind".equals(fieldName)) {
+ deserializedData.type = reader.getString();
+ } else {
+ reader.skipChildren();
+ }
+ }
+
+ return deserializedData;
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataRequest.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataRequest.java
new file mode 100644
index 00000000000..09b384ab05d
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataRequest.java
@@ -0,0 +1,94 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Fluent;
+import com.azure.core.annotation.Generated;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonSerializable;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * summary of Request
+ *
+ * description of Request.
+ */
+@Fluent
+public final class DataRequest implements JsonSerializable {
+ /*
+ * The parameters property.
+ */
+ @Generated
+ private RequestParameters parameters;
+
+ /**
+ * Creates an instance of DataRequest class.
+ */
+ @Generated
+ public DataRequest() {
+ }
+
+ /**
+ * Get the parameters property: The parameters property.
+ *
+ * @return the parameters value.
+ */
+ @Generated
+ public RequestParameters getParameters() {
+ return this.parameters;
+ }
+
+ /**
+ * Set the parameters property: The parameters property.
+ *
+ * @param parameters the parameters value to set.
+ * @return the DataRequest object itself.
+ */
+ @Generated
+ public DataRequest setParameters(RequestParameters parameters) {
+ this.parameters = parameters;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeJsonField("parameters", this.parameters);
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of DataRequest from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of DataRequest if the JsonReader was pointing to an instance of it, or null if it was
+ * pointing to JSON null.
+ * @throws IOException If an error occurs while reading the DataRequest.
+ */
+ @Generated
+ public static DataRequest fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ DataRequest deserializedDataRequest = new DataRequest();
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("parameters".equals(fieldName)) {
+ deserializedDataRequest.parameters = RequestParameters.fromJson(reader);
+ } else {
+ reader.skipChildren();
+ }
+ }
+
+ return deserializedDataRequest;
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataResponse.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataResponse.java
new file mode 100644
index 00000000000..62bb2ac0c28
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataResponse.java
@@ -0,0 +1,189 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.Immutable;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonSerializable;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * summary of Response
+ *
+ * description of Response. Include tab ' ' in doc.
+ */
+@Immutable
+public final class DataResponse implements JsonSerializable {
+ /*
+ * summary of name property
+ *
+ * description of name property
+ */
+ @Generated
+ private final String name;
+
+ /*
+ * summary of data property
+ *
+ * description of data property
+ */
+ @Generated
+ private final BinaryData data;
+
+ /*
+ * summary of type property
+ *
+ * description of type property
+ */
+ @Generated
+ private final TypesModel dataType;
+
+ /*
+ * summary of status property
+ *
+ * description of status property
+ */
+ @Generated
+ private final DataStatus status;
+
+ /*
+ * The anonymous property.
+ */
+ @Generated
+ private final RunObject anonymous;
+
+ /**
+ * Creates an instance of DataResponse class.
+ *
+ * @param name the name value to set.
+ * @param data the data value to set.
+ * @param dataType the dataType value to set.
+ * @param status the status value to set.
+ * @param anonymous the anonymous value to set.
+ */
+ @Generated
+ private DataResponse(String name, BinaryData data, TypesModel dataType, DataStatus status, RunObject anonymous) {
+ this.name = name;
+ this.data = data;
+ this.dataType = dataType;
+ this.status = status;
+ this.anonymous = anonymous;
+ }
+
+ /**
+ * Get the name property: summary of name property
+ *
+ * description of name property.
+ *
+ * @return the name value.
+ */
+ @Generated
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Get the data property: summary of data property
+ *
+ * description of data property.
+ *
+ * @return the data value.
+ */
+ @Generated
+ public BinaryData getData() {
+ return this.data;
+ }
+
+ /**
+ * Get the dataType property: summary of type property
+ *
+ * description of type property.
+ *
+ * @return the dataType value.
+ */
+ @Generated
+ public TypesModel getDataType() {
+ return this.dataType;
+ }
+
+ /**
+ * Get the status property: summary of status property
+ *
+ * description of status property.
+ *
+ * @return the status value.
+ */
+ @Generated
+ public DataStatus getStatus() {
+ return this.status;
+ }
+
+ /**
+ * Get the anonymous property: The anonymous property.
+ *
+ * @return the anonymous value.
+ */
+ @Generated
+ public RunObject getAnonymous() {
+ return this.anonymous;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeStringField("name", this.name);
+ jsonWriter.writeJsonField("data", this.data);
+ jsonWriter.writeStringField("type", this.dataType == null ? null : this.dataType.toString());
+ jsonWriter.writeStringField("status", this.status == null ? null : this.status.toString());
+ jsonWriter.writeJsonField("anonymous", this.anonymous);
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of DataResponse from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of DataResponse if the JsonReader was pointing to an instance of it, or null if it was
+ * pointing to JSON null.
+ * @throws IllegalStateException If the deserialized JSON object was missing any required properties.
+ * @throws IOException If an error occurs while reading the DataResponse.
+ */
+ @Generated
+ public static DataResponse fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ String name = null;
+ BinaryData data = null;
+ TypesModel dataType = null;
+ DataStatus status = null;
+ RunObject anonymous = null;
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("name".equals(fieldName)) {
+ name = reader.getString();
+ } else if ("data".equals(fieldName)) {
+ data = BinaryData.fromJson(reader);
+ } else if ("type".equals(fieldName)) {
+ dataType = TypesModel.fromString(reader.getString());
+ } else if ("status".equals(fieldName)) {
+ status = DataStatus.fromString(reader.getString());
+ } else if ("anonymous".equals(fieldName)) {
+ anonymous = RunObject.fromJson(reader);
+ } else {
+ reader.skipChildren();
+ }
+ }
+ return new DataResponse(name, data, dataType, status, anonymous);
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataStatus.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataStatus.java
new file mode 100644
index 00000000000..298965620ec
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/DataStatus.java
@@ -0,0 +1,65 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.util.ExpandableStringEnum;
+import java.util.Collection;
+
+/**
+ * summary of Statuses
+ *
+ * description of Statuses.
+ */
+public final class DataStatus extends ExpandableStringEnum {
+ /**
+ * Static value Running for DataStatus.
+ */
+ @Generated
+ public static final DataStatus LRO_RUNNING = fromString("Running");
+
+ /**
+ * Static value Completed for DataStatus.
+ */
+ @Generated
+ public static final DataStatus COMPLETED = fromString("Completed");
+
+ /**
+ * Static value Failed for DataStatus.
+ */
+ @Generated
+ public static final DataStatus FAILED = fromString("Failed");
+
+ /**
+ * Creates a new instance of DataStatus value.
+ *
+ * @deprecated Use the {@link #fromString(String)} factory method.
+ */
+ @Generated
+ @Deprecated
+ public DataStatus() {
+ }
+
+ /**
+ * Creates or finds a DataStatus from its string representation.
+ *
+ * @param name a name to look for.
+ * @return the corresponding DataStatus.
+ */
+ @Generated
+ public static DataStatus fromString(String name) {
+ return fromString(name, DataStatus.class);
+ }
+
+ /**
+ * Gets known DataStatus values.
+ *
+ * @return known DataStatus values.
+ */
+ @Generated
+ public static Collection values() {
+ return values(DataStatus.class);
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/GetAnonymousResponse.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/GetAnonymousResponse.java
new file mode 100644
index 00000000000..b30416d7a36
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/GetAnonymousResponse.java
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.Immutable;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonSerializable;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * The GetAnonymousResponse model.
+ */
+@Immutable
+public final class GetAnonymousResponse implements JsonSerializable {
+ /*
+ * The name property.
+ */
+ @Generated
+ private final String name;
+
+ /**
+ * Creates an instance of GetAnonymousResponse class.
+ *
+ * @param name the name value to set.
+ */
+ @Generated
+ private GetAnonymousResponse(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name property: The name property.
+ *
+ * @return the name value.
+ */
+ @Generated
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeStringField("name", this.name);
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of GetAnonymousResponse from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of GetAnonymousResponse if the JsonReader was pointing to an instance of it, or null if it
+ * was pointing to JSON null.
+ * @throws IllegalStateException If the deserialized JSON object was missing any required properties.
+ * @throws IOException If an error occurs while reading the GetAnonymousResponse.
+ */
+ @Generated
+ public static GetAnonymousResponse fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ String name = null;
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("name".equals(fieldName)) {
+ name = reader.getString();
+ } else {
+ reader.skipChildren();
+ }
+ }
+ return new GetAnonymousResponse(name);
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RequestParameters.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RequestParameters.java
new file mode 100644
index 00000000000..efbeadac620
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RequestParameters.java
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.Immutable;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonSerializable;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * The RequestParameters model.
+ */
+@Immutable
+public final class RequestParameters implements JsonSerializable {
+ /*
+ * The type property.
+ */
+ @Generated
+ private final RequestParametersType type;
+
+ /**
+ * Creates an instance of RequestParameters class.
+ *
+ * @param type the type value to set.
+ */
+ @Generated
+ public RequestParameters(RequestParametersType type) {
+ this.type = type;
+ }
+
+ /**
+ * Get the type property: The type property.
+ *
+ * @return the type value.
+ */
+ @Generated
+ public RequestParametersType getType() {
+ return this.type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeStringField("type", this.type == null ? null : this.type.toString());
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of RequestParameters from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of RequestParameters if the JsonReader was pointing to an instance of it, or null if it was
+ * pointing to JSON null.
+ * @throws IllegalStateException If the deserialized JSON object was missing any required properties.
+ * @throws IOException If an error occurs while reading the RequestParameters.
+ */
+ @Generated
+ public static RequestParameters fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ RequestParametersType type = null;
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("type".equals(fieldName)) {
+ type = RequestParametersType.fromString(reader.getString());
+ } else {
+ reader.skipChildren();
+ }
+ }
+ return new RequestParameters(type);
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RequestParametersType.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RequestParametersType.java
new file mode 100644
index 00000000000..9874d82d88d
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RequestParametersType.java
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+/**
+ * Defines values for RequestParametersType.
+ */
+public enum RequestParametersType {
+ /**
+ * Enum value Type1.
+ */
+ TYPE1("Type1"),
+
+ /**
+ * Enum value Type2.
+ */
+ TYPE2("Type2");
+
+ /**
+ * The actual serialized value for a RequestParametersType instance.
+ */
+ private final String value;
+
+ RequestParametersType(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Parses a serialized value to a RequestParametersType instance.
+ *
+ * @param value the serialized value to parse.
+ * @return the parsed RequestParametersType object, or null if unable to parse.
+ */
+ public static RequestParametersType fromString(String value) {
+ if (value == null) {
+ return null;
+ }
+ RequestParametersType[] items = RequestParametersType.values();
+ for (RequestParametersType item : items) {
+ if (item.toString().equalsIgnoreCase(value)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return this.value;
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObject.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObject.java
new file mode 100644
index 00000000000..684c706c96c
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObject.java
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.Immutable;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonSerializable;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * The RunObject model.
+ */
+@Immutable
+public final class RunObject implements JsonSerializable {
+ /*
+ * The last_error property.
+ */
+ @Generated
+ private final RunObjectLastError1 lastError;
+
+ /**
+ * Creates an instance of RunObject class.
+ *
+ * @param lastError the lastError value to set.
+ */
+ @Generated
+ private RunObject(RunObjectLastError1 lastError) {
+ this.lastError = lastError;
+ }
+
+ /**
+ * Get the lastError property: The last_error property.
+ *
+ * @return the lastError value.
+ */
+ @Generated
+ public RunObjectLastError1 getLastError() {
+ return this.lastError;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeJsonField("last_error", this.lastError);
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of RunObject from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of RunObject if the JsonReader was pointing to an instance of it, or null if it was pointing
+ * to JSON null.
+ * @throws IllegalStateException If the deserialized JSON object was missing any required properties.
+ * @throws IOException If an error occurs while reading the RunObject.
+ */
+ @Generated
+ public static RunObject fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ RunObjectLastError1 lastError = null;
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("last_error".equals(fieldName)) {
+ lastError = RunObjectLastError1.fromJson(reader);
+ } else {
+ reader.skipChildren();
+ }
+ }
+ return new RunObject(lastError);
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObjectLastError1.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObjectLastError1.java
new file mode 100644
index 00000000000..06de8bd7eb2
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObjectLastError1.java
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+import com.azure.core.annotation.Generated;
+import com.azure.core.annotation.Immutable;
+import com.azure.json.JsonReader;
+import com.azure.json.JsonSerializable;
+import com.azure.json.JsonToken;
+import com.azure.json.JsonWriter;
+import java.io.IOException;
+
+/**
+ * The RunObjectLastError1 model.
+ */
+@Immutable
+public final class RunObjectLastError1 implements JsonSerializable {
+ /*
+ * The code property.
+ */
+ @Generated
+ private final RunObjectLastErrorCode code;
+
+ /**
+ * Creates an instance of RunObjectLastError1 class.
+ *
+ * @param code the code value to set.
+ */
+ @Generated
+ private RunObjectLastError1(RunObjectLastErrorCode code) {
+ this.code = code;
+ }
+
+ /**
+ * Get the code property: The code property.
+ *
+ * @return the code value.
+ */
+ @Generated
+ public RunObjectLastErrorCode getCode() {
+ return this.code;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Generated
+ @Override
+ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.writeStartObject();
+ jsonWriter.writeStringField("code", this.code == null ? null : this.code.toString());
+ return jsonWriter.writeEndObject();
+ }
+
+ /**
+ * Reads an instance of RunObjectLastError1 from the JsonReader.
+ *
+ * @param jsonReader The JsonReader being read.
+ * @return An instance of RunObjectLastError1 if the JsonReader was pointing to an instance of it, or null if it was
+ * pointing to JSON null.
+ * @throws IllegalStateException If the deserialized JSON object was missing any required properties.
+ * @throws IOException If an error occurs while reading the RunObjectLastError1.
+ */
+ @Generated
+ public static RunObjectLastError1 fromJson(JsonReader jsonReader) throws IOException {
+ return jsonReader.readObject(reader -> {
+ RunObjectLastErrorCode code = null;
+ while (reader.nextToken() != JsonToken.END_OBJECT) {
+ String fieldName = reader.getFieldName();
+ reader.nextToken();
+
+ if ("code".equals(fieldName)) {
+ code = RunObjectLastErrorCode.fromString(reader.getString());
+ } else {
+ reader.skipChildren();
+ }
+ }
+ return new RunObjectLastError1(code);
+ });
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObjectLastErrorCode.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObjectLastErrorCode.java
new file mode 100644
index 00000000000..6aa0ead6ebf
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/RunObjectLastErrorCode.java
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+/**
+ * Defines values for RunObjectLastErrorCode.
+ */
+public enum RunObjectLastErrorCode {
+ /**
+ * Enum value server_error.
+ */
+ SERVER_ERROR("server_error"),
+
+ /**
+ * Enum value rate_limit_exceeded.
+ */
+ RATE_LIMIT_EXCEEDED("rate_limit_exceeded"),
+
+ /**
+ * Enum value invalid_prompt.
+ */
+ INVALID_PROMPT("invalid_prompt");
+
+ /**
+ * The actual serialized value for a RunObjectLastErrorCode instance.
+ */
+ private final String value;
+
+ RunObjectLastErrorCode(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Parses a serialized value to a RunObjectLastErrorCode instance.
+ *
+ * @param value the serialized value to parse.
+ * @return the parsed RunObjectLastErrorCode object, or null if unable to parse.
+ */
+ public static RunObjectLastErrorCode fromString(String value) {
+ if (value == null) {
+ return null;
+ }
+ RunObjectLastErrorCode[] items = RunObjectLastErrorCode.values();
+ for (RunObjectLastErrorCode item : items) {
+ if (item.toString().equalsIgnoreCase(value)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return this.value;
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/TypesModel.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/TypesModel.java
new file mode 100644
index 00000000000..5177a618296
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/TypesModel.java
@@ -0,0 +1,58 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.models;
+
+/**
+ * summary of Types
+ *
+ * description of Types.
+ */
+public enum TypesModel {
+ /**
+ * Enum value Blob.
+ */
+ BLOB("Blob"),
+
+ /**
+ * Enum value File.
+ */
+ FILE("File");
+
+ /**
+ * The actual serialized value for a TypesModel instance.
+ */
+ private final String value;
+
+ TypesModel(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Parses a serialized value to a TypesModel instance.
+ *
+ * @param value the serialized value to parse.
+ * @return the parsed TypesModel object, or null if unable to parse.
+ */
+ public static TypesModel fromString(String value) {
+ if (value == null) {
+ return null;
+ }
+ TypesModel[] items = TypesModel.values();
+ for (TypesModel item : items) {
+ if (item.toString().equalsIgnoreCase(value)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return this.value;
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/package-info.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/package-info.java
new file mode 100644
index 00000000000..54d07be7724
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/models/package-info.java
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+/**
+ *
+ * Package containing the data models for NamingJavaParser.
+ * description of Naming JavaParser.
+ *
+ */
+package tsptest.namingjavaparser.models;
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/package-info.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/package-info.java
new file mode 100644
index 00000000000..a9b955b3184
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/namingjavaparser/package-info.java
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+/**
+ *
+ * Package containing the classes for NamingJavaParser.
+ * description of Naming JavaParser.
+ *
+ */
+package tsptest.namingjavaparser;
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/tsptest-namingjavaparser_apiview_properties.json b/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/tsptest-namingjavaparser_apiview_properties.json
new file mode 100644
index 00000000000..b9a1873b662
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/tsptest-namingjavaparser_apiview_properties.json
@@ -0,0 +1,29 @@
+{
+ "flavor": "Azure",
+ "CrossLanguageDefinitionId": {
+ "tsptest.namingjavaparser.NamingJavaParserAsyncClient": "TspTest.NamingJavaParser.NamingOp",
+ "tsptest.namingjavaparser.NamingJavaParserAsyncClient.getAnonymous": "TspTest.NamingJavaParser.NamingOp.getAnonymous",
+ "tsptest.namingjavaparser.NamingJavaParserAsyncClient.getAnonymousWithResponse": "TspTest.NamingJavaParser.NamingOp.getAnonymous",
+ "tsptest.namingjavaparser.NamingJavaParserAsyncClient.post": "TspTest.NamingJavaParser.NamingOp.post",
+ "tsptest.namingjavaparser.NamingJavaParserAsyncClient.postWithResponse": "TspTest.NamingJavaParser.NamingOp.post",
+ "tsptest.namingjavaparser.NamingJavaParserClient": "TspTest.NamingJavaParser.NamingOp",
+ "tsptest.namingjavaparser.NamingJavaParserClient.getAnonymous": "TspTest.NamingJavaParser.NamingOp.getAnonymous",
+ "tsptest.namingjavaparser.NamingJavaParserClient.getAnonymousWithResponse": "TspTest.NamingJavaParser.NamingOp.getAnonymous",
+ "tsptest.namingjavaparser.NamingJavaParserClient.post": "TspTest.NamingJavaParser.NamingOp.post",
+ "tsptest.namingjavaparser.NamingJavaParserClient.postWithResponse": "TspTest.NamingJavaParser.NamingOp.post",
+ "tsptest.namingjavaparser.NamingJavaParserClientBuilder": "TspTest.NamingJavaParser",
+ "tsptest.namingjavaparser.models.BinaryData": "TspTest.NamingJavaParser.DataModel",
+ "tsptest.namingjavaparser.models.BytesData": "TspTest.NamingJavaParser.BytesData",
+ "tsptest.namingjavaparser.models.Data": "TspTest.NamingJavaParser.Data",
+ "tsptest.namingjavaparser.models.DataRequest": "TspTest.NamingJavaParser.Request",
+ "tsptest.namingjavaparser.models.DataResponse": "TspTest.NamingJavaParser.Response",
+ "tsptest.namingjavaparser.models.DataStatus": "TspTest.NamingJavaParser.StatusModel",
+ "tsptest.namingjavaparser.models.GetAnonymousResponse": "TspTest.NamingJavaParser.getAnonymous.Response.anonymous",
+ "tsptest.namingjavaparser.models.RequestParameters": "TspTest.NamingJavaParser.Request.parameters.anonymous",
+ "tsptest.namingjavaparser.models.RequestParametersType": "TspTest.NamingJavaParser.Request.parameters.type.anonymous",
+ "tsptest.namingjavaparser.models.RunObject": "TspTest.NamingJavaParser.RunObject",
+ "tsptest.namingjavaparser.models.RunObjectLastError1": "TspTest.NamingJavaParser.RunObject.last_error.anonymous",
+ "tsptest.namingjavaparser.models.RunObjectLastErrorCode": "TspTest.NamingJavaParser.RunObject.last_error.code.anonymous",
+ "tsptest.namingjavaparser.models.TypesModel": "TspTest.NamingJavaParser.TypesModel"
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/tsptest-namingjavaparser_metadata.json b/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/tsptest-namingjavaparser_metadata.json
new file mode 100644
index 00000000000..a3ae86747fe
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/resources/META-INF/tsptest-namingjavaparser_metadata.json
@@ -0,0 +1 @@
+{"flavor":"Azure","crossLanguageDefinitions":{"tsptest.namingjavaparser.NamingJavaParserAsyncClient":"TspTest.NamingJavaParser.NamingOp","tsptest.namingjavaparser.NamingJavaParserAsyncClient.getAnonymous":"TspTest.NamingJavaParser.NamingOp.getAnonymous","tsptest.namingjavaparser.NamingJavaParserAsyncClient.getAnonymousWithResponse":"TspTest.NamingJavaParser.NamingOp.getAnonymous","tsptest.namingjavaparser.NamingJavaParserAsyncClient.post":"TspTest.NamingJavaParser.NamingOp.post","tsptest.namingjavaparser.NamingJavaParserAsyncClient.postWithResponse":"TspTest.NamingJavaParser.NamingOp.post","tsptest.namingjavaparser.NamingJavaParserClient":"TspTest.NamingJavaParser.NamingOp","tsptest.namingjavaparser.NamingJavaParserClient.getAnonymous":"TspTest.NamingJavaParser.NamingOp.getAnonymous","tsptest.namingjavaparser.NamingJavaParserClient.getAnonymousWithResponse":"TspTest.NamingJavaParser.NamingOp.getAnonymous","tsptest.namingjavaparser.NamingJavaParserClient.post":"TspTest.NamingJavaParser.NamingOp.post","tsptest.namingjavaparser.NamingJavaParserClient.postWithResponse":"TspTest.NamingJavaParser.NamingOp.post","tsptest.namingjavaparser.NamingJavaParserClientBuilder":"TspTest.NamingJavaParser","tsptest.namingjavaparser.models.BinaryData":"TspTest.NamingJavaParser.DataModel","tsptest.namingjavaparser.models.BytesData":"TspTest.NamingJavaParser.BytesData","tsptest.namingjavaparser.models.Data":"TspTest.NamingJavaParser.Data","tsptest.namingjavaparser.models.DataRequest":"TspTest.NamingJavaParser.Request","tsptest.namingjavaparser.models.DataResponse":"TspTest.NamingJavaParser.Response","tsptest.namingjavaparser.models.DataStatus":"TspTest.NamingJavaParser.StatusModel","tsptest.namingjavaparser.models.GetAnonymousResponse":"TspTest.NamingJavaParser.getAnonymous.Response.anonymous","tsptest.namingjavaparser.models.RequestParameters":"TspTest.NamingJavaParser.Request.parameters.anonymous","tsptest.namingjavaparser.models.RequestParametersType":"TspTest.NamingJavaParser.Request.parameters.type.anonymous","tsptest.namingjavaparser.models.RunObject":"TspTest.NamingJavaParser.RunObject","tsptest.namingjavaparser.models.RunObjectLastError1":"TspTest.NamingJavaParser.RunObject.last_error.anonymous","tsptest.namingjavaparser.models.RunObjectLastErrorCode":"TspTest.NamingJavaParser.RunObject.last_error.code.anonymous","tsptest.namingjavaparser.models.TypesModel":"TspTest.NamingJavaParser.TypesModel"}}
\ No newline at end of file
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/resources/tsptest-namingjavaparser.properties b/packages/http-client-java/generator/http-client-generator-test/src/main/resources/tsptest-namingjavaparser.properties
new file mode 100644
index 00000000000..ca812989b4f
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/resources/tsptest-namingjavaparser.properties
@@ -0,0 +1,2 @@
+name=${project.artifactId}
+version=${project.version}
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/test/java/tsptest/namingjavaparser/generated/NamingJavaParserClientTestBase.java b/packages/http-client-java/generator/http-client-generator-test/src/test/java/tsptest/namingjavaparser/generated/NamingJavaParserClientTestBase.java
new file mode 100644
index 00000000000..3f5054bbec1
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/src/test/java/tsptest/namingjavaparser/generated/NamingJavaParserClientTestBase.java
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+// Code generated by Microsoft (R) TypeSpec Code Generator.
+
+package tsptest.namingjavaparser.generated;
+
+// The Java test files under 'generated' package are generated for your reference.
+// If you wish to modify these files, please copy them out of the 'generated' package, and modify there.
+// See https://aka.ms/azsdk/dpg/java/tests for guide on adding a test.
+
+import com.azure.core.http.policy.HttpLogDetailLevel;
+import com.azure.core.http.policy.HttpLogOptions;
+import com.azure.core.test.TestMode;
+import com.azure.core.test.TestProxyTestBase;
+import com.azure.core.util.Configuration;
+import tsptest.namingjavaparser.NamingJavaParserClient;
+import tsptest.namingjavaparser.NamingJavaParserClientBuilder;
+
+class NamingJavaParserClientTestBase extends TestProxyTestBase {
+ protected NamingJavaParserClient namingJavaParserClient;
+
+ @Override
+ protected void beforeTest() {
+ NamingJavaParserClientBuilder namingJavaParserClientbuilder = new NamingJavaParserClientBuilder()
+ .endpoint(Configuration.getGlobalConfiguration().get("ENDPOINT", "endpoint"))
+ .httpClient(getHttpClientOrUsePlayback(getHttpClients().findFirst().orElse(null)))
+ .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BASIC));
+ if (getTestMode() == TestMode.RECORD) {
+ namingJavaParserClientbuilder.addPolicy(interceptorManager.getRecordPolicy());
+ }
+ namingJavaParserClient = namingJavaParserClientbuilder.buildClient();
+
+ }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-test/tsp/naming-javaparser.tsp b/packages/http-client-java/generator/http-client-generator-test/tsp/naming-javaparser.tsp
new file mode 100644
index 00000000000..c7eaaa76fc8
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-test/tsp/naming-javaparser.tsp
@@ -0,0 +1,128 @@
+import "@typespec/rest";
+import "@azure-tools/typespec-azure-core";
+import "@azure-tools/typespec-client-generator-core";
+
+using TypeSpec.Http;
+using Azure.Core;
+using Azure.Core.Foundations;
+using Azure.ClientGenerator.Core;
+
+@summary("summary of Naming JavaParser")
+@doc("description of Naming JavaParser")
+@service(#{ title: "Naming JavaParser" })
+namespace TspTest.NamingJavaParser;
+
+@summary("summary of Response")
+@doc("description of Response. Include tab ' ' in doc.")
+@friendlyName("DataResponse")
+model Response {
+ @summary("summary of name property")
+ @doc("description of name property")
+ name: string;
+
+ @summary("summary of data property")
+ @doc("description of data property")
+ data: DataModel;
+
+ @summary("summary of type property")
+ @doc("description of type property")
+ @friendlyName("dataType")
+ type: TypesModel;
+
+ @summary("summary of status property")
+ @doc("description of status property")
+ status: StatusModel;
+
+ anonymous: RunObject;
+}
+
+model RunObject {
+ last_error: {
+ code: "server_error" | "rate_limit_exceeded" | "invalid_prompt";
+ } | null;
+}
+
+@summary("summary of Data")
+@doc("description of Data")
+@friendlyName("BinaryData")
+model DataModel {
+ @summary("summary of data property")
+ @doc("description of data property")
+ data: Data;
+}
+
+// "@" should be escaped
+@doc("Dummy doc to make the javadoc break at the 'at' symbol. The type of the Data depends on @data.kind.letusmakeitlongsoitwouldbreakbeforethis field")
+@discriminator("kind")
+model Data {
+ // "@" should be escaped
+ @clientName("type")
+ @encodedName("application/json", "@data.kind")
+ kind: string;
+}
+
+model BytesData extends Data {
+ @clientName("type")
+ @encodedName("application/json", "@data.kind")
+ kind: "bytes";
+
+ // "{@code}" should not be escaped
+ @doc("Data as {@code byte[]}")
+ @clientName("dataAsBytes")
+ @encodedName("application/json", "data_bytes")
+ data: bytes;
+}
+
+#suppress "@azure-tools/typespec-azure-core/use-extensible-enum" "For testing"
+@summary("summary of Types")
+@doc("description of Types")
+enum TypesModel {
+ Blob,
+ File,
+}
+
+@summary("summary of Statuses")
+@doc("description of Statuses")
+@friendlyName("DataStatus")
+union StatusModel {
+ string,
+
+ @clientName("LroRunning")
+ Running: "Running",
+
+ Completed: "Completed",
+ Failed: "Failed",
+}
+
+@summary("summary of Request")
+@doc("description of Request")
+@friendlyName("DataRequest")
+model Request {
+ @summary("summary of name query parameter")
+ @doc("description of name query parameter")
+ @query
+ name: string;
+
+ @header
+ @summary("summary of etag header parameter")
+ @doc("description of etag header parameter")
+ etag?: string;
+
+ parameters?: {
+ type: "Type1" | "Type2";
+ };
+}
+
+@summary("summary of naming route")
+@doc("description of naming route")
+@route("/naming")
+interface NamingOp {
+ @summary("summary of POST op")
+ @doc("description of POST op")
+ @post
+ post(@bodyRoot body: Request): Response | ErrorResponse;
+
+ getAnonymous(): {
+ name: string;
+ };
+}
diff --git a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java
index 7e1eab677aa..cb534b4e1d9 100644
--- a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java
+++ b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java
@@ -161,7 +161,7 @@ private static void handleDPG(CodeModel codeModel, EmitterOptions emitterOptions
// handle customization
// write output
// java files
- new Postprocessor(typeSpecPlugin).postProcess(javaFiles);
+ new Postprocessor(typeSpecPlugin, settings.isUseEclipseLanguageServer()).postProcess(javaFiles);
// XML include POM
javaPackage.getXmlFiles()
diff --git a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/TypeSpecPlugin.java b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/TypeSpecPlugin.java
index 513fbcf43cf..1904cb44bff 100644
--- a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/TypeSpecPlugin.java
+++ b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/TypeSpecPlugin.java
@@ -228,6 +228,9 @@ public TypeSpecPlugin(EmitterOptions options, boolean sdkIntegration) {
if (options.getUseObjectForUnknown()) {
SETTINGS_MAP.put("use-object-for-unknown", emitterOptions.getUseObjectForUnknown());
}
+ if (options.getUseEclipseLanguageServer() != null) {
+ SETTINGS_MAP.put("use-eclipse-language-server", emitterOptions.getUseEclipseLanguageServer());
+ }
if (options.getUseRestProxy() != null) {
SETTINGS_MAP.put("use-rest-proxy", emitterOptions.getUseRestProxy());
}
diff --git a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/fluent/TypeSpecFluentPlugin.java b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/fluent/TypeSpecFluentPlugin.java
index f909b20b37d..96e241a3550 100644
--- a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/fluent/TypeSpecFluentPlugin.java
+++ b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/fluent/TypeSpecFluentPlugin.java
@@ -69,6 +69,9 @@ public TypeSpecFluentPlugin(EmitterOptions emitterOptions, boolean sdkIntegratio
if (emitterOptions.getRenameModel() != null) {
SETTINGS_MAP.put("rename-model", emitterOptions.getRenameModel());
}
+ if (emitterOptions.getUseEclipseLanguageServer() != null) {
+ SETTINGS_MAP.put("use-eclipse-language-server", emitterOptions.getUseEclipseLanguageServer());
+ }
JavaSettingsAccessor.setHost(this);
LOGGER.info("Output folder: {}", emitterOptions.getOutputDir());
diff --git a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/model/EmitterOptions.java b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/model/EmitterOptions.java
index a6374cb895f..7d76c886246 100644
--- a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/model/EmitterOptions.java
+++ b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/model/EmitterOptions.java
@@ -31,6 +31,7 @@ public class EmitterOptions implements JsonSerializable {
private Boolean includeApiViewProperties = true;
private String packageVersion;
private Boolean useObjectForUnknown = false;
+ private Boolean useEclipseLanguageServer = true;
private Map polling = new HashMap<>();
private String modelsSubpackage;
private String apiVersion;
@@ -89,6 +90,10 @@ public Boolean getUseObjectForUnknown() {
return useObjectForUnknown;
}
+ public Boolean getUseEclipseLanguageServer() {
+ return useEclipseLanguageServer;
+ }
+
public List getServiceVersions() {
return serviceVersions;
}
@@ -187,6 +192,8 @@ public static EmitterOptions fromJson(JsonReader jsonReader) throws IOException
options.includeApiViewProperties = reader.getNullable(EmitterOptions::getBoolean);
} else if ("use-object-for-unknown".equals(fieldName)) {
options.useObjectForUnknown = reader.getNullable(EmitterOptions::getBoolean);
+ } else if ("use-eclipse-language-server".equals(fieldName)) {
+ options.useEclipseLanguageServer = reader.getNullable(EmitterOptions::getBoolean);
} else if ("polling".equals(fieldName)) {
options.polling = reader.readMap(PollingSettings::fromJson);
} else if ("arm".equals(fieldName)) {