diff --git a/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml b/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml
index 18abe9430410..e5f2cafd3123 100644
--- a/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml
+++ b/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml
@@ -426,5 +426,8 @@
+
+
+
diff --git a/eng/lintingconfigs/checkstyle/track2/checkstyle.xml b/eng/lintingconfigs/checkstyle/track2/checkstyle.xml
index 6c6c6082878e..ea5bc3edb785 100644
--- a/eng/lintingconfigs/checkstyle/track2/checkstyle.xml
+++ b/eng/lintingconfigs/checkstyle/track2/checkstyle.xml
@@ -420,5 +420,8 @@
+
+
+
diff --git a/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml b/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml
index 6432e8cc91ba..efd19a736c8a 100644
--- a/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml
+++ b/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml
@@ -425,5 +425,8 @@
+
+
+
diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt
index 57c7afc7de10..f5278c2caf7c 100644
--- a/eng/versioning/version_client.txt
+++ b/eng/versioning/version_client.txt
@@ -549,11 +549,12 @@ io.clientcore:optional-dependency-tests;1.0.0-beta.1;1.0.0-beta.1
# In the pom, the version update tag after the version should name the unreleased package and the dependency version:
#
+unreleased_com.azure.resourcemanager:azure-resourcemanager-containerregistry;2.55.0
unreleased_com.azure.v2:azure-core;2.0.0-beta.1
unreleased_com.azure.v2:azure-identity;2.0.0-beta.1
unreleased_com.azure.v2:azure-data-appconfiguration;2.0.0-beta.1
unreleased_io.clientcore:http-netty4;1.0.0-beta.1
-unreleased_com.azure.resourcemanager:azure-resourcemanager-containerregistry;2.55.0
+unreleased_io.clientcore:linting-extensions;1.0.0-beta.2
# Released Beta dependencies: Copy the entry from above, prepend "beta_", remove the current
# version and set the version to the released beta. Released beta dependencies are only valid
diff --git a/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml
index b1b2b42206a4..6929837bf10e 100644
--- a/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml
+++ b/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml
@@ -3,22 +3,24 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
+
+
+
-
-
+
+
+
diff --git a/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml
index c3ba6b3d5de1..4be50bb27716 100644
--- a/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml
+++ b/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml
@@ -5,12 +5,12 @@
-
-
+
+
-
-
-
+
+
+
diff --git a/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml
index e4db86f0597a..f5dc6622661d 100644
--- a/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml
+++ b/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml
@@ -3,17 +3,17 @@
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
+
+
diff --git a/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml
index b564c4aedda0..9a31b590b18b 100644
--- a/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml
+++ b/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml
@@ -3,12 +3,12 @@
-
-
+
+
-
-
-
+
+
+
diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/quickpulse/model/QuickPulseMonitoringDataPoints.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/quickpulse/model/QuickPulseMonitoringDataPoints.java
index f22590bb5c9e..60ed7a3654f5 100644
--- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/quickpulse/model/QuickPulseMonitoringDataPoints.java
+++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/quickpulse/model/QuickPulseMonitoringDataPoints.java
@@ -1,5 +1,6 @@
package com.azure.monitor.opentelemetry.exporter.implementation.quickpulse.model;
+import com.azure.json.JsonReader;
import com.azure.json.JsonSerializable;
import com.azure.json.JsonWriter;
@@ -17,4 +18,9 @@ public QuickPulseMonitoringDataPoints(List monitoringDataPoi
public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
return jsonWriter.writeArray(monitoringDataPoints, JsonWriter::writeJson, false);
}
+
+ public static QuickPulseMonitoringDataPoints fromJson(JsonReader jsonReader) throws IOException {
+ List dataPoints = jsonReader.readArray(QuickPulseEnvelope::fromJson);
+ return new QuickPulseMonitoringDataPoints(dataPoints);
+ }
}
diff --git a/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingAsyncSample.java b/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingAsyncSample.java
index 3d6974d6da83..3d92143391f1 100644
--- a/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingAsyncSample.java
+++ b/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingAsyncSample.java
@@ -25,7 +25,16 @@
@JsonClassDescription("Gets the quality of the given SDK.")
class GetSdkQualityAsync {
@JsonPropertyDescription("The name of the SDK.")
- public String name;
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public GetSdkQualityAsync setName(String name) {
+ this.name = name;
+ return this;
+ }
public String execute() {
return name.contains("OpenAI") ? "Excellent quality and robust implementation!" : "Unknown quality";
diff --git a/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingSample.java b/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingSample.java
index 8b432c4750a9..c68dac190df3 100644
--- a/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingSample.java
+++ b/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingSample.java
@@ -25,7 +25,16 @@
@JsonClassDescription("Gets the quality of the given SDK.")
class GetSdkQuality {
@JsonPropertyDescription("The name of the SDK.")
- public String name;
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public GetSdkQuality setName(String name) {
+ this.name = name;
+ return this;
+ }
public String execute() {
return name.contains("OpenAI") ? "Excellent quality and robust implementation!" : "Unknown quality";
diff --git a/sdk/openai/azure-ai-openai-stainless/src/test/java/com/azure/ai/openai/stainless/OpenAIOkHttpClientTestBase.java b/sdk/openai/azure-ai-openai-stainless/src/test/java/com/azure/ai/openai/stainless/OpenAIOkHttpClientTestBase.java
index 6283d2fa2810..68d603730b60 100644
--- a/sdk/openai/azure-ai-openai-stainless/src/test/java/com/azure/ai/openai/stainless/OpenAIOkHttpClientTestBase.java
+++ b/sdk/openai/azure-ai-openai-stainless/src/test/java/com/azure/ai/openai/stainless/OpenAIOkHttpClientTestBase.java
@@ -69,10 +69,28 @@
@JsonClassDescription("Get the current weather in a given location")
class GetCurrentWeather {
@JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
- public String location;
+ private String location;
@JsonPropertyDescription("Temperature unit (celsius or fahrenheit)")
- public String unit = "celsius";
+ private String unit = "celsius";
+
+ public String getLocation() {
+ return location;
+ }
+
+ public GetCurrentWeather setLocation(String location) {
+ this.location = location;
+ return this;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+ public GetCurrentWeather setUnit(String unit) {
+ this.unit = unit;
+ return this;
+ }
public String execute() {
return "The weather in " + location + " is 72 degrees " + unit;
diff --git a/sdk/parents/azure-client-sdk-parent-v2/pom.xml b/sdk/parents/azure-client-sdk-parent-v2/pom.xml
index a8ee8c08ff77..5d38ac091892 100644
--- a/sdk/parents/azure-client-sdk-parent-v2/pom.xml
+++ b/sdk/parents/azure-client-sdk-parent-v2/pom.xml
@@ -708,7 +708,7 @@
io.clientcore
linting-extensions
- 1.0.0-beta.1
+ 1.0.0-beta.2
com.puppycrawl.tools
diff --git a/sdk/parents/azure-client-sdk-parent/pom.xml b/sdk/parents/azure-client-sdk-parent/pom.xml
index 680523d726fd..d4bb259f2310 100644
--- a/sdk/parents/azure-client-sdk-parent/pom.xml
+++ b/sdk/parents/azure-client-sdk-parent/pom.xml
@@ -720,7 +720,7 @@
io.clientcore
linting-extensions
- 1.0.0-beta.1
+ 1.0.0-beta.2
com.puppycrawl.tools
diff --git a/sdk/parents/azure-data-sdk-parent/pom.xml b/sdk/parents/azure-data-sdk-parent/pom.xml
index 4288c6d213a7..216d5c5b2bf4 100644
--- a/sdk/parents/azure-data-sdk-parent/pom.xml
+++ b/sdk/parents/azure-data-sdk-parent/pom.xml
@@ -78,7 +78,7 @@
io.clientcore
linting-extensions
- 1.0.0-beta.1
+ 1.0.0-beta.2
com.puppycrawl.tools
diff --git a/sdk/parents/clientcore-parent/pom.xml b/sdk/parents/clientcore-parent/pom.xml
index 333fe1c6d6aa..83ef7489e96a 100644
--- a/sdk/parents/clientcore-parent/pom.xml
+++ b/sdk/parents/clientcore-parent/pom.xml
@@ -788,7 +788,7 @@
io.clientcore
linting-extensions
- 1.0.0-beta.1
+ 1.0.0-beta.2
com.puppycrawl.tools
diff --git a/sdk/search/azure-search-documents/checkstyle-suppressions.xml b/sdk/search/azure-search-documents/checkstyle-suppressions.xml
index 47a4f50ab30c..49c0537c61ec 100644
--- a/sdk/search/azure-search-documents/checkstyle-suppressions.xml
+++ b/sdk/search/azure-search-documents/checkstyle-suppressions.xml
@@ -15,4 +15,6 @@
+
+
diff --git a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java
new file mode 100644
index 000000000000..33ee79601fa3
--- /dev/null
+++ b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java
@@ -0,0 +1,173 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package io.clientcore.linting.extensions.checkstyle.checks;
+
+import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
+import com.puppycrawl.tools.checkstyle.api.DetailAST;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Verifies that classes implementing JsonSerializable or XmlSerializable provide a static fromJson or fromXml method.
+ */
+public class SerializableMethodsCheck extends AbstractCheck {
+ static final String ERR_NO_FROM_JSON = "Class implementing JsonSerializable must provide a static fromJson method.";
+ static final String ERR_NO_FROM_XML = "Class implementing XmlSerializable must provide a static fromXml method.";
+
+ private List snapshotArchive;
+
+ public SerializableMethodsCheck() {
+ }
+
+ @Override
+ public int[] getDefaultTokens() {
+ return getRequiredTokens();
+ }
+
+ @Override
+ public int[] getAcceptableTokens() {
+ return getRequiredTokens();
+ }
+
+ @Override
+ public int[] getRequiredTokens() {
+ return new int[] { TokenTypes.CLASS_DEF, TokenTypes.METHOD_DEF };
+ }
+
+ @Override
+ public void beginTree(DetailAST rootNode) {
+ snapshotArchive = new ArrayList<>();
+ }
+
+ @Override
+ public void visitToken(DetailAST currentNode) {
+ int tokenKind = currentNode.getType();
+
+ if (tokenKind == TokenTypes.CLASS_DEF) {
+ TypeSnapshot snapshot = captureTypeSnapshot(currentNode);
+ snapshotArchive.add(snapshot);
+ } else if (tokenKind == TokenTypes.METHOD_DEF) {
+ integrateMethodIntoSnapshot(currentNode);
+ }
+ }
+
+ @Override
+ public void leaveToken(DetailAST currentNode) {
+ if (currentNode.getType() == TokenTypes.CLASS_DEF) {
+ performSnapshotAudit(currentNode);
+ }
+ }
+
+ private TypeSnapshot captureTypeSnapshot(DetailAST classNode) {
+ TypeSnapshot snapshot = new TypeSnapshot();
+ snapshot.classNode = classNode;
+
+ // Check if the class is abstract - skip validation for abstract types
+ snapshot.isAbstract = isAbstractType(classNode);
+
+ DetailAST interfaceSection = classNode.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE);
+ if (interfaceSection != null) {
+ digestInterfaceSection(interfaceSection, snapshot);
+ }
+
+ return snapshot;
+ }
+
+ private boolean isAbstractType(DetailAST classNode) {
+ DetailAST modifierBlock = classNode.findFirstToken(TokenTypes.MODIFIERS);
+ if (modifierBlock == null) {
+ return false;
+ }
+ return modifierBlock.findFirstToken(TokenTypes.ABSTRACT) != null;
+ }
+
+ private void digestInterfaceSection(DetailAST interfaceSection, TypeSnapshot snapshot) {
+ DetailAST cursor = interfaceSection.getFirstChild();
+
+ while (cursor != null) {
+ if (cursor.getType() == TokenTypes.IDENT) {
+ String interfaceLabel = cursor.getText();
+ if ("JsonSerializable".equals(interfaceLabel)) {
+ snapshot.implementsJsonSerializable = true;
+ } else if ("XmlSerializable".equals(interfaceLabel)) {
+ snapshot.implementsXmlSerializable = true;
+ }
+ }
+ cursor = cursor.getNextSibling();
+ }
+ }
+
+ private void integrateMethodIntoSnapshot(DetailAST methodNode) {
+ if (snapshotArchive.isEmpty()) {
+ return;
+ }
+
+ TypeSnapshot latestSnapshot = snapshotArchive.get(snapshotArchive.size() - 1);
+
+ if (!latestSnapshot.implementsJsonSerializable && !latestSnapshot.implementsXmlSerializable) {
+ return;
+ }
+
+ DetailAST nameNode = methodNode.findFirstToken(TokenTypes.IDENT);
+ if (nameNode == null) {
+ return;
+ }
+
+ String methodLabel = nameNode.getText();
+ boolean markedStatic = probeForStaticMarker(methodNode);
+
+ latestSnapshot.digestMethod(methodLabel, markedStatic);
+ }
+
+ private boolean probeForStaticMarker(DetailAST methodNode) {
+ DetailAST modifierBlock = methodNode.findFirstToken(TokenTypes.MODIFIERS);
+
+ if (modifierBlock == null) {
+ return false;
+ }
+
+ return modifierBlock.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
+ }
+
+ private void performSnapshotAudit(DetailAST classNode) {
+ if (snapshotArchive.isEmpty()) {
+ return;
+ }
+
+ TypeSnapshot snapshot = snapshotArchive.remove(snapshotArchive.size() - 1);
+
+ // Skip validation for abstract types
+ if (snapshot.isAbstract) {
+ return;
+ }
+
+ if (snapshot.implementsJsonSerializable && !snapshot.observedFromJson) {
+ log(classNode, ERR_NO_FROM_JSON);
+ }
+
+ if (snapshot.implementsXmlSerializable && !snapshot.observedFromXml) {
+ log(classNode, ERR_NO_FROM_XML);
+ }
+ }
+
+ private static class TypeSnapshot {
+ DetailAST classNode;
+ boolean isAbstract;
+ boolean extendsAnotherType;
+ boolean implementsJsonSerializable;
+ boolean implementsXmlSerializable;
+ boolean observedFromJson;
+ boolean observedFromXml;
+
+ void digestMethod(String methodLabel, boolean markedStatic) {
+ if ("fromJson".equals(methodLabel) && markedStatic) {
+ observedFromJson = true;
+ } else if ("fromXml".equals(methodLabel) && markedStatic) {
+ observedFromXml = true;
+ }
+ }
+ }
+}
diff --git a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java
index e39617454a2e..a1e378620f23 100644
--- a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java
+++ b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java
@@ -46,6 +46,7 @@ public class ServiceClientCheck extends AbstractCheck {
private static final String IS_ASYNC = "isAsync";
private static final String CONTEXT = "Context";
private static final String REQUEST_OPTIONS = "RequestOptions";
+ private static final String REQUEST_CONTEXT = "RequestContext";
private static final String RESPONSE_BRACKET = "Response<";
private static final String MONO_BRACKET = "Mono<";
@@ -77,7 +78,7 @@ public class ServiceClientCheck extends AbstractCheck {
private static final String ASYNC_CONTEXT_ERROR
= "Asynchronous method with annotation @ServiceMethod must not have ''%s'' as a method parameter.";
private static final String SYNC_CONTEXT_ERROR
- = "Synchronous method with annotation @ServiceMethod must have ''%s'' or ''%s'' as a method parameter.";
+ = "Synchronous method with annotation @ServiceMethod must have ''%s'', ''%s'' or ''%s'' as a method parameter.";
// Add all imported classes into a map, key is the name of class and value is the full package path of class.
private final Map simpleClassNameToQualifiedNameMap = new HashMap<>();
@@ -399,6 +400,16 @@ private void checkContextInRightPlace(DetailAST methodDefToken) {
return paramTypeIdentToken != null && REQUEST_OPTIONS.equals(paramTypeIdentToken.getText());
}).isPresent();
+ boolean containsRequestContextParameter
+ = TokenUtil.findFirstTokenByPredicate(parametersToken, parameterToken -> {
+ if (parameterToken.getType() != TokenTypes.PARAMETER_DEF) {
+ return false;
+ }
+ final DetailAST paramTypeIdentToken
+ = parameterToken.findFirstToken(TokenTypes.TYPE).findFirstToken(TokenTypes.IDENT);
+ return paramTypeIdentToken != null && REQUEST_CONTEXT.equals(paramTypeIdentToken.getText());
+ }).isPresent();
+
if (containsContextParameter) {
// MONO and PagedFlux return type implies Asynchronous method
if (returnType.startsWith(MONO_BRACKET)
@@ -406,11 +417,11 @@ private void checkContextInRightPlace(DetailAST methodDefToken) {
|| returnType.startsWith(POLLER_FLUX_BRACKET)) {
log(methodDefToken, String.format(ASYNC_CONTEXT_ERROR, CONTEXT));
}
- } else if (!containsRequestOptionsParameter) {
+ } else if (!(containsRequestOptionsParameter || containsRequestContextParameter)) {
// Context or RequestOptions should be passed in as an argument to all public methods
// annotated with @ServiceMethod that return Response in sync clients.
if (returnType.startsWith(RESPONSE_BRACKET)) {
- log(methodDefToken, String.format(SYNC_CONTEXT_ERROR, CONTEXT, REQUEST_OPTIONS));
+ log(methodDefToken, String.format(SYNC_CONTEXT_ERROR, CONTEXT, REQUEST_OPTIONS, REQUEST_CONTEXT));
}
}
}
diff --git a/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java
new file mode 100644
index 000000000000..b9aca71a0728
--- /dev/null
+++ b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java
@@ -0,0 +1,227 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package io.clientcore.linting.extensions.checkstyle.checks;
+
+import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
+import com.puppycrawl.tools.checkstyle.Checker;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_FROM_JSON;
+import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_FROM_XML;
+
+/**
+ * Tests {@link SerializableMethodsCheck}.
+ */
+public class SerializableMethodsCheckTest extends AbstractModuleTestSupport {
+ private Checker lintingChecker;
+
+ @BeforeEach
+ public void setupChecker() throws Exception {
+ lintingChecker = createChecker(createModuleConfig(SerializableMethodsCheck.class));
+ }
+
+ @AfterEach
+ public void teardownChecker() {
+ lintingChecker.destroy();
+ }
+
+ @Override
+ protected String getPackageLocation() {
+ return "io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck";
+ }
+
+ @Test
+ public void jsonSerializableWithBothMethods() throws Exception {
+ File testFile = TestUtils.createCheckFile("jsonComplete", "package com.azure;",
+ "public class JsonComplete implements JsonSerializable {", " public void toJson() {}",
+ " public static JsonComplete fromJson() { return null; }", "}");
+
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void jsonSerializableMissingFromJson() throws Exception {
+ File testFile = TestUtils.createCheckFile("jsonMissingFrom", "package com.azure;",
+ "public class JsonMissingFrom implements JsonSerializable {", " public void toJson() {}", "}");
+
+ String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void jsonSerializableWithNonStaticFromJson() throws Exception {
+ File testFile = TestUtils.createCheckFile("jsonNonStaticFrom", "package com.azure;",
+ "public class JsonNonStaticFrom implements JsonSerializable {", " public void toJson() {}",
+ " public JsonNonStaticFrom fromJson() { return null; }", "}");
+
+ String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void xmlSerializableWithBothMethods() throws Exception {
+ File testFile = TestUtils.createCheckFile("xmlComplete", "package com.azure;",
+ "public class XmlComplete implements XmlSerializable {", " public void toXml() {}",
+ " public static XmlComplete fromXml() { return null; }", "}");
+
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void xmlSerializableMissingFromXml() throws Exception {
+ File testFile = TestUtils.createCheckFile("xmlMissingFrom", "package com.azure;",
+ "public class XmlMissingFrom implements XmlSerializable {", " public void toXml() {}", "}");
+
+ String[] expectedErrors = { "2:1: " + ERR_NO_FROM_XML };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void xmlSerializableWithNonStaticFromXml() throws Exception {
+ File testFile = TestUtils.createCheckFile("xmlNonStaticFrom", "package com.azure;",
+ "public class XmlNonStaticFrom implements XmlSerializable {", " public void toXml() {}",
+ " public XmlNonStaticFrom fromXml() { return null; }", "}");
+
+ String[] expectedErrors = { "2:1: " + ERR_NO_FROM_XML };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void bothInterfacesWithAllMethods() throws Exception {
+ File testFile = TestUtils.createCheckFile("bothComplete", "package com.azure;",
+ "public class BothComplete implements JsonSerializable, XmlSerializable {", " public void toJson() {}",
+ " public static BothComplete fromJson() { return null; }", " public void toXml() {}",
+ " public static BothComplete fromXml() { return null; }", "}");
+
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void bothInterfacesMissingAllMethods() throws Exception {
+ File testFile = TestUtils.createCheckFile("bothMissing", "package com.azure;",
+ "public class BothMissing implements JsonSerializable, XmlSerializable {", "}");
+
+ String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON, "2:1: " + ERR_NO_FROM_XML };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void bothInterfacesMissingJsonMethods() throws Exception {
+ File testFile = TestUtils.createCheckFile("bothMissingJson", "package com.azure;",
+ "public class BothMissingJson implements JsonSerializable, XmlSerializable {", " public void toXml() {}",
+ " public static BothMissingJson fromXml() { return null; }", "}");
+
+ String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void bothInterfacesMissingXmlMethods() throws Exception {
+ File testFile = TestUtils.createCheckFile("bothMissingXml", "package com.azure;",
+ "public class BothMissingXml implements JsonSerializable, XmlSerializable {", " public void toJson() {}",
+ " public static BothMissingXml fromJson() { return null; }", "}");
+
+ String[] expectedErrors = { "2:1: " + ERR_NO_FROM_XML };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void classNotImplementingInterface() throws Exception {
+ File testFile = TestUtils.createCheckFile("noInterface", "package com.azure;", "public class NoInterface {",
+ " public void someMethod() {}", "}");
+
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void nestedClassWithJsonSerializable() throws Exception {
+ File testFile = TestUtils.createCheckFile("nestedJson", "package com.azure;", "public class OuterClass {",
+ " public static class InnerClass implements JsonSerializable {", " public void toJson() {}",
+ " public static InnerClass fromJson() { return null; }", " }", "}");
+
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void nestedClassMissingMethods() throws Exception {
+ File testFile = TestUtils.createCheckFile("nestedMissing", "package com.azure;", "public class OuterClass {",
+ " public static class InnerClass implements JsonSerializable {", " }", "}");
+
+ String[] expectedErrors = { "3:5: " + ERR_NO_FROM_JSON };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void classWithExtraMethodsAndCorrectSerializationMethods() throws Exception {
+ File testFile = TestUtils.createCheckFile("extraMethods", "package com.azure;",
+ "public class ExtraMethods implements JsonSerializable {", " public void toJson() {}",
+ " public static ExtraMethods fromJson() { return null; }", " public void otherMethod() {}",
+ " public String getData() { return null; }", "}");
+
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void abstractClassImplementingJsonSerializable() throws Exception {
+ File testFile = TestUtils.createCheckFile("abstractJson", "package com.azure;",
+ "public abstract class AbstractJson implements JsonSerializable {", " public void toJson() {}", "}");
+
+ // Abstract classes should not require fromJson method
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void abstractClassImplementingXmlSerializable() throws Exception {
+ File testFile = TestUtils.createCheckFile("abstractXml", "package com.azure;",
+ "public abstract class AbstractXml implements XmlSerializable {", " public void toXml() {}", "}");
+
+ // Abstract classes should not require fromXml method
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void abstractClassImplementingBothInterfaces() throws Exception {
+ File testFile = TestUtils.createCheckFile("abstractBoth", "package com.azure;",
+ "public abstract class AbstractBoth implements JsonSerializable, XmlSerializable {",
+ " public void toJson() {}", " public void toXml() {}", "}");
+
+ // Abstract classes should not require fromJson or fromXml methods
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void classExtendsAnotherTypeAndImplementsJsonSerializable() throws Exception {
+ File testFile = TestUtils.createCheckFile("extendsJson", "package com.azure;",
+ "public class ExtendsJson extends BaseClass implements JsonSerializable {", " public void toJson() {}",
+ "}");
+
+ // Classes extending another type and implementing JsonSerializable should require fromJson method
+ String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON };
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors);
+ }
+
+ @Test
+ public void classExtendsTypeAndImplementsJsonSerializableWithFromJson() throws Exception {
+ File testFile = TestUtils.createCheckFile("concreteExtendsAbstract", "package com.azure;",
+ "public class ConcreteClass extends AbstractBase implements JsonSerializable {",
+ " public void toJson() {}", " public static ConcreteClass fromJson() { return null; }", "}");
+
+ // Classes extending any type should pass (validation is skipped)
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+
+ @Test
+ public void nestedAbstractClassImplementingJsonSerializable() throws Exception {
+ File testFile = TestUtils.createCheckFile("nestedAbstract", "package com.azure;", "public class OuterClass {",
+ " public abstract static class InnerAbstract implements JsonSerializable {",
+ " public void toJson() {}", " }", "}");
+
+ // Nested abstract classes should not require fromJson method
+ verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath());
+ }
+}