This is an automated email from the ASF dual-hosted git repository.

veithen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-axiom.git


The following commit(s) were added to refs/heads/master by this push:
     new 02d06cc92 Add @StringOrCharacterData annotation and Checker Framework 
checker
02d06cc92 is described below

commit 02d06cc92524ee01d3fea33c146e495d31134a1f
Author: Copilot <[email protected]>
AuthorDate: Wed Jun 3 18:27:17 2026 +0200

    Add @StringOrCharacterData annotation and Checker Framework checker
    
    Co-authored-by: Andreas Veithen-Knowles <[email protected]>
---
 buildutils/pom.xml                                 |   1 +
 .../stringorcharacterdata-checker}/pom.xml         |  17 ++-
 .../StringOrCharacterDataAnnotatedTypeFactory.java | 122 +++++++++++++++++++++
 .../checker/StringOrCharacterDataChecker.java      |  23 ++--
 components/{ => checker-annotations}/pom.xml       |  19 ++--
 .../stream/annotations/StringOrCharacterData.java  |  38 +++++++
 .../annotations/UnknownCharacterDataType.java      |  28 +++--
 components/core-streams/pom.xml                    |   6 +
 .../apache/axiom/core/stream/CharacterData.java    |   2 +
 .../org/apache/axiom/core/stream/XmlHandler.java   |   4 +-
 components/pom.xml                                 |   1 +
 mixins/core-mixins/pom.xml                         |   6 +
 .../apache/axiom/core/CoreCharacterDataNode.java   |   5 +-
 .../axiom/core/CoreCharacterDataSinkNode.java      |   4 +-
 .../axiom/core/CoreCharacterDataSourceNode.java    |   3 +
 .../axiom/core/CoreMixedContentContainer.java      |   3 +
 .../java/org/apache/axiom/core/CoreParentNode.java |   4 +-
 .../axiom/core/impl/AbstractNodeIterator.java      |   1 +
 .../org/apache/axiom/core/impl/TreeWalkerImpl.java |   8 +-
 .../axiom/core/impl/builder/BuildableContext.java  |   5 +-
 .../axiom/core/impl/builder/BuilderHandler.java    |   3 +-
 .../apache/axiom/core/impl/builder/Context.java    |   3 +-
 .../axiom/core/impl/builder/UnwrappingContext.java |   3 +-
 ...CoreCharacterDataContainingParentNodeMixin.java |   5 +-
 .../impl/mixin/CoreCharacterDataNodeMixin.java     |  11 +-
 .../impl/mixin/CoreMixedContentContainerMixin.java |   4 +-
 .../axiom/core/impl/mixin/CoreParentNodeMixin.java |  22 +++-
 pom.xml                                            |  30 +++++
 28 files changed, 312 insertions(+), 69 deletions(-)

diff --git a/buildutils/pom.xml b/buildutils/pom.xml
index 8eb899775..7bf8a2081 100644
--- a/buildutils/pom.xml
+++ b/buildutils/pom.xml
@@ -36,6 +36,7 @@
         <module>axiom-weaver-maven-plugin</module>
         <module>buildutils-maven-plugin</module>
         <module>shade-axiom-xml</module>
+        <module>stringorcharacterdata-checker</module>
     </modules>
 
     <properties>
diff --git a/mixins/core-mixins/pom.xml 
b/buildutils/stringorcharacterdata-checker/pom.xml
similarity index 77%
copy from mixins/core-mixins/pom.xml
copy to buildutils/stringorcharacterdata-checker/pom.xml
index a0710fc17..cac55858b 100644
--- a/mixins/core-mixins/pom.xml
+++ b/buildutils/stringorcharacterdata-checker/pom.xml
@@ -22,30 +22,27 @@
 
     <parent>
         <groupId>org.apache.ws.commons.axiom</groupId>
-        <artifactId>mixins</artifactId>
+        <artifactId>buildutils</artifactId>
         <version>2.1.0-SNAPSHOT</version>
     </parent>
 
-    <artifactId>core-mixins</artifactId>
+    <artifactId>stringorcharacterdata-checker</artifactId>
 
-    <name>Core Mixins</name>
     <url>http://ws.apache.org/axiom/</url>
 
     <dependencies>
         <dependency>
-            <groupId>commons-logging</groupId>
-            <artifactId>commons-logging</artifactId>
+            <groupId>org.checkerframework</groupId>
+            <artifactId>checker</artifactId>
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
-            <artifactId>core-streams</artifactId>
+            <artifactId>checker-annotations</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>axiom-weaver-annotations</artifactId>
-            <version>${project.version}</version>
-            <scope>provided</scope>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service-annotations</artifactId>
         </dependency>
     </dependencies>
 </project>
diff --git 
a/buildutils/stringorcharacterdata-checker/src/main/java/org/apache/axiom/checker/StringOrCharacterDataAnnotatedTypeFactory.java
 
b/buildutils/stringorcharacterdata-checker/src/main/java/org/apache/axiom/checker/StringOrCharacterDataAnnotatedTypeFactory.java
new file mode 100644
index 000000000..000b89231
--- /dev/null
+++ 
b/buildutils/stringorcharacterdata-checker/src/main/java/org/apache/axiom/checker/StringOrCharacterDataAnnotatedTypeFactory.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.axiom.checker;
+
+import java.lang.annotation.Annotation;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
+import org.apache.axiom.core.stream.annotations.UnknownCharacterDataType;
+import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
+import org.checkerframework.common.basetype.BaseTypeChecker;
+import org.checkerframework.framework.type.AnnotatedTypeMirror;
+import org.checkerframework.framework.type.QualifierHierarchy;
+import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
+import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
+import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator;
+import org.checkerframework.framework.type.typeannotator.TypeAnnotator;
+import org.checkerframework.javacutil.AnnotationBuilder;
+import org.checkerframework.javacutil.TypesUtils;
+
+/**
+ * The annotated type factory for the {@link StringOrCharacterDataChecker}. 
This factory
+ * automatically adds {@code @StringOrCharacterData} to {@link String} types 
and to types that
+ * implement {@link org.apache.axiom.core.stream.CharacterData}.
+ */
+public class StringOrCharacterDataAnnotatedTypeFactory extends 
BaseAnnotatedTypeFactory {
+
+    private static final String CHARACTER_DATA_NAME = 
"org.apache.axiom.core.stream.CharacterData";
+
+    private final AnnotationMirror STRING_OR_CHARACTER_DATA;
+
+    public StringOrCharacterDataAnnotatedTypeFactory(BaseTypeChecker checker) {
+        super(checker);
+        STRING_OR_CHARACTER_DATA = AnnotationBuilder.fromClass(elements, 
StringOrCharacterData.class);
+        postInit();
+    }
+
+    @Override
+    protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() 
{
+        LinkedHashSet<Class<? extends Annotation>> qualifiers = new 
LinkedHashSet<>();
+        qualifiers.add(StringOrCharacterData.class);
+        qualifiers.add(UnknownCharacterDataType.class);
+        return qualifiers;
+    }
+
+    @Override
+    protected TreeAnnotator createTreeAnnotator() {
+        return new ListTreeAnnotator(super.createTreeAnnotator());
+    }
+
+    @Override
+    protected TypeAnnotator createTypeAnnotator() {
+        return new ListTypeAnnotator(new 
StringOrCharacterDataTypeAnnotator(this), super.createTypeAnnotator());
+    }
+
+    private class StringOrCharacterDataTypeAnnotator extends TypeAnnotator {
+
+        
StringOrCharacterDataTypeAnnotator(StringOrCharacterDataAnnotatedTypeFactory 
factory) {
+            super(factory);
+        }
+
+        @Override
+        public Void visitDeclared(AnnotatedTypeMirror.AnnotatedDeclaredType 
type, Void p) {
+            if (shouldAnnotate(type)) {
+                type.addAnnotation(STRING_OR_CHARACTER_DATA);
+            }
+            return super.visitDeclared(type, null);
+        }
+    }
+
+    private boolean shouldAnnotate(AnnotatedTypeMirror.AnnotatedDeclaredType 
type) {
+        QualifierHierarchy hierarchy = getQualifierHierarchy();
+        Set<AnnotationMirror> annotations = type.getAnnotations();
+        // Don't override existing annotations
+        for (AnnotationMirror anno : annotations) {
+            if (hierarchy.isSubtypeQualifiersOnly(anno, 
STRING_OR_CHARACTER_DATA)
+                    || 
hierarchy.isSubtypeQualifiersOnly(STRING_OR_CHARACTER_DATA, anno)) {
+                return false;
+            }
+        }
+        TypeMirror underlyingType = type.getUnderlyingType();
+        if (TypesUtils.isString(underlyingType)) {
+            return true;
+        }
+        return isCharacterData(underlyingType);
+    }
+
+    private boolean isCharacterData(TypeMirror type) {
+        if (type instanceof DeclaredType declaredType) {
+            String qualifiedName = 
TypesUtils.getQualifiedName(declaredType).toString();
+            if (qualifiedName.equals(CHARACTER_DATA_NAME)) {
+                return true;
+            }
+            // Check supertypes
+            for (TypeMirror superType : types.directSupertypes(type)) {
+                if (isCharacterData(superType)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
 
b/buildutils/stringorcharacterdata-checker/src/main/java/org/apache/axiom/checker/StringOrCharacterDataChecker.java
similarity index 60%
copy from 
mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
copy to 
buildutils/stringorcharacterdata-checker/src/main/java/org/apache/axiom/checker/StringOrCharacterDataChecker.java
index 6077327fd..e51466c7c 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
+++ 
b/buildutils/stringorcharacterdata-checker/src/main/java/org/apache/axiom/checker/StringOrCharacterDataChecker.java
@@ -16,17 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.axiom.core.impl.mixin;
+package org.apache.axiom.checker;
 
-import org.apache.axiom.core.CoreMixedContentContainer;
-import org.apache.axiom.core.CoreModelException;
-import org.apache.axiom.core.ElementAction;
-import org.apache.axiom.weaver.annotation.Mixin;
+import com.google.auto.service.AutoService;
+import javax.annotation.processing.Processor;
+import org.checkerframework.common.basetype.BaseTypeChecker;
 
-@Mixin
-public abstract class CoreMixedContentContainerMixin implements 
CoreMixedContentContainer {
-    @Override
-    public final Object coreGetCharacterData(ElementAction elementAction) 
throws CoreModelException {
-        return internalGetCharacterData(elementAction);
-    }
-}
+/**
+ * A Checker Framework checker that enforces that {@code 
@StringOrCharacterData Object} can only be
+ * assigned from {@link String}, {@link 
org.apache.axiom.core.stream.CharacterData}, or another
+ * {@code @StringOrCharacterData Object}.
+ */
+@AutoService(Processor.class)
+public class StringOrCharacterDataChecker extends BaseTypeChecker {}
diff --git a/components/pom.xml b/components/checker-annotations/pom.xml
similarity index 78%
copy from components/pom.xml
copy to components/checker-annotations/pom.xml
index 6d945feb7..c4ac604b4 100644
--- a/components/pom.xml
+++ b/components/checker-annotations/pom.xml
@@ -22,21 +22,18 @@
 
     <parent>
         <groupId>org.apache.ws.commons.axiom</groupId>
-        <artifactId>axiom</artifactId>
+        <artifactId>components</artifactId>
         <version>2.1.0-SNAPSHOT</version>
     </parent>
 
-    <artifactId>components</artifactId>
-    <packaging>pom</packaging>
+    <artifactId>checker-annotations</artifactId>
 
     <url>http://ws.apache.org/axiom/</url>
 
-    <modules>
-        <module>base64-utils</module>
-        <module>blob</module>
-        <module>core-streams</module>
-        <module>io</module>
-        <module>namespace-utils</module>
-        <module>xml-utils</module>
-    </modules>
+    <dependencies>
+        <dependency>
+            <groupId>org.checkerframework</groupId>
+            <artifactId>checker-qual</artifactId>
+        </dependency>
+    </dependencies>
 </project>
diff --git 
a/components/checker-annotations/src/main/java/org/apache/axiom/core/stream/annotations/StringOrCharacterData.java
 
b/components/checker-annotations/src/main/java/org/apache/axiom/core/stream/annotations/StringOrCharacterData.java
new file mode 100644
index 000000000..a2a34fd95
--- /dev/null
+++ 
b/components/checker-annotations/src/main/java/org/apache/axiom/core/stream/annotations/StringOrCharacterData.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.axiom.core.stream.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.checkerframework.framework.qual.SubtypeOf;
+
+/**
+ * Indicates that the annotated {@link Object} type use holds a value that is 
either a {@link
+ * String} or a {@link org.apache.axiom.core.stream.CharacterData} instance.
+ *
+ * <p>This annotation is enforced by the {@code StringOrCharacterDataChecker} 
which ensures that
+ * only {@link String}, {@link org.apache.axiom.core.stream.CharacterData}, or 
another {@code
+ * @StringOrCharacterData Object} can be assigned to a position annotated with 
this qualifier.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE_USE})
+@SubtypeOf(UnknownCharacterDataType.class)
+public @interface StringOrCharacterData {}
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
 
b/components/checker-annotations/src/main/java/org/apache/axiom/core/stream/annotations/UnknownCharacterDataType.java
similarity index 54%
copy from 
mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
copy to 
components/checker-annotations/src/main/java/org/apache/axiom/core/stream/annotations/UnknownCharacterDataType.java
index 6077327fd..33ba01ab0 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
+++ 
b/components/checker-annotations/src/main/java/org/apache/axiom/core/stream/annotations/UnknownCharacterDataType.java
@@ -16,17 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.axiom.core.impl.mixin;
+package org.apache.axiom.core.stream.annotations;
 
-import org.apache.axiom.core.CoreMixedContentContainer;
-import org.apache.axiom.core.CoreModelException;
-import org.apache.axiom.core.ElementAction;
-import org.apache.axiom.weaver.annotation.Mixin;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.checkerframework.framework.qual.DefaultQualifierInHierarchy;
+import org.checkerframework.framework.qual.SubtypeOf;
 
-@Mixin
-public abstract class CoreMixedContentContainerMixin implements 
CoreMixedContentContainer {
-    @Override
-    public final Object coreGetCharacterData(ElementAction elementAction) 
throws CoreModelException {
-        return internalGetCharacterData(elementAction);
-    }
-}
+/**
+ * The top type in the StringOrCharacterData type hierarchy. This is the 
default qualifier for
+ * unannotated types and represents an unknown character data type.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE_USE})
+@SubtypeOf({})
+@DefaultQualifierInHierarchy
+public @interface UnknownCharacterDataType {}
diff --git a/components/core-streams/pom.xml b/components/core-streams/pom.xml
index 954b7b926..931aee9d8 100644
--- a/components/core-streams/pom.xml
+++ b/components/core-streams/pom.xml
@@ -35,6 +35,12 @@
             <groupId>commons-logging</groupId>
             <artifactId>commons-logging</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>checker-annotations</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>xml-utils</artifactId>
diff --git 
a/components/core-streams/src/main/java/org/apache/axiom/core/stream/CharacterData.java
 
b/components/core-streams/src/main/java/org/apache/axiom/core/stream/CharacterData.java
index f66906b94..d9c7d2a06 100644
--- 
a/components/core-streams/src/main/java/org/apache/axiom/core/stream/CharacterData.java
+++ 
b/components/core-streams/src/main/java/org/apache/axiom/core/stream/CharacterData.java
@@ -19,6 +19,7 @@
 package org.apache.axiom.core.stream;
 
 import java.io.IOException;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 
 // TODO: clean up this Javadoc
 /**
@@ -49,5 +50,6 @@ public interface CharacterData {
      * @return a {@link String} or {@link CharacterData} representation of 
this instance that
      *     remains valid beyond the current invocation
      */
+    @StringOrCharacterData
     Object retain();
 }
diff --git 
a/components/core-streams/src/main/java/org/apache/axiom/core/stream/XmlHandler.java
 
b/components/core-streams/src/main/java/org/apache/axiom/core/stream/XmlHandler.java
index 3ce6f42d9..bf66d885c 100644
--- 
a/components/core-streams/src/main/java/org/apache/axiom/core/stream/XmlHandler.java
+++ 
b/components/core-streams/src/main/java/org/apache/axiom/core/stream/XmlHandler.java
@@ -18,6 +18,8 @@
  */
 package org.apache.axiom.core.stream;
 
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
+
 public interface XmlHandler {
     void startDocument(String inputEncoding, String xmlVersion, String 
xmlEncoding, Boolean standalone)
             throws StreamException;
@@ -121,7 +123,7 @@ public interface XmlHandler {
      *     otherwise
      * @throws StreamException if an error occurs when processing the event
      */
-    void processCharacterData(Object data, boolean ignorable) throws 
StreamException;
+    void processCharacterData(@StringOrCharacterData Object data, boolean 
ignorable) throws StreamException;
 
     /**
      * Notify the handler of the beginning of a processing instruction.
diff --git a/components/pom.xml b/components/pom.xml
index 6d945feb7..26bd1b9f5 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -34,6 +34,7 @@
     <modules>
         <module>base64-utils</module>
         <module>blob</module>
+        <module>checker-annotations</module>
         <module>core-streams</module>
         <module>io</module>
         <module>namespace-utils</module>
diff --git a/mixins/core-mixins/pom.xml b/mixins/core-mixins/pom.xml
index a0710fc17..7ca286b79 100644
--- a/mixins/core-mixins/pom.xml
+++ b/mixins/core-mixins/pom.xml
@@ -36,6 +36,12 @@
             <groupId>commons-logging</groupId>
             <artifactId>commons-logging</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>checker-annotations</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>core-streams</artifactId>
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataNode.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataNode.java
index 46e0affcf..ce5d9c2c5 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataNode.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataNode.java
@@ -18,11 +18,14 @@
  */
 package org.apache.axiom.core;
 
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
+
 public interface CoreCharacterDataNode extends CoreLeafNode, 
CoreCharacterDataContainer {
     @Override
+    @StringOrCharacterData
     Object coreGetCharacterData();
 
-    void coreSetCharacterData(Object data);
+    void coreSetCharacterData(@StringOrCharacterData Object data);
 
     /**
      * Check whether this text node contains element content whitespace (also 
called "ignorable
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataSinkNode.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataSinkNode.java
index c935e7664..26c4efbdc 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataSinkNode.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataSinkNode.java
@@ -18,6 +18,8 @@
  */
 package org.apache.axiom.core;
 
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
+
 public interface CoreCharacterDataSinkNode {
-    void coreSetCharacterData(Object data, Semantics semantics) throws 
CoreModelException;
+    void coreSetCharacterData(@StringOrCharacterData Object data, Semantics 
semantics) throws CoreModelException;
 }
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataSourceNode.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataSourceNode.java
index 2270f2685..9c147e10f 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataSourceNode.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreCharacterDataSourceNode.java
@@ -18,6 +18,9 @@
  */
 package org.apache.axiom.core;
 
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
+
 public interface CoreCharacterDataSourceNode extends CoreNode {
+    @StringOrCharacterData
     Object coreGetCharacterData() throws CoreModelException;
 }
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreMixedContentContainer.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreMixedContentContainer.java
index f2c56db13..a68497dda 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreMixedContentContainer.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreMixedContentContainer.java
@@ -18,6 +18,9 @@
  */
 package org.apache.axiom.core;
 
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
+
 public interface CoreMixedContentContainer extends CoreParentNode, 
CoreCharacterDataSinkNode {
+    @StringOrCharacterData
     Object coreGetCharacterData(ElementAction elementAction) throws 
CoreModelException;
 }
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreParentNode.java 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreParentNode.java
index 73b22cf52..2792de09e 100644
--- a/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreParentNode.java
+++ b/mixins/core-mixins/src/main/java/org/apache/axiom/core/CoreParentNode.java
@@ -21,6 +21,7 @@ package org.apache.axiom.core;
 import org.apache.axiom.core.stream.StreamException;
 import org.apache.axiom.core.stream.XmlHandler;
 import org.apache.axiom.core.stream.XmlReader;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 
 /** Interface for parent nodes. */
 public interface CoreParentNode extends CoreNode {
@@ -101,7 +102,7 @@ public interface CoreParentNode extends CoreNode {
             Mapper<S, ? super T> mapper,
             Semantics semantics);
 
-    void coreSetCharacterData(Object data, Semantics semantics) throws 
CoreModelException;
+    void coreSetCharacterData(@StringOrCharacterData Object data, Semantics 
semantics) throws CoreModelException;
 
     void coreRemoveChildren(Semantics semantics) throws CoreModelException;
 
@@ -117,5 +118,6 @@ public interface CoreParentNode extends CoreNode {
 
     Content internalGetContent(boolean create);
 
+    @StringOrCharacterData
     Object internalGetCharacterData(ElementAction elementAction) throws 
CoreModelException;
 }
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/AbstractNodeIterator.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/AbstractNodeIterator.java
index d19103cf3..943b20aa1 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/AbstractNodeIterator.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/AbstractNodeIterator.java
@@ -136,6 +136,7 @@ public abstract class AbstractNodeIterator<T extends 
CoreNode, S> implements Nod
     }
 
     @Override
+    @SuppressWarnings("stringorcharacterdata")
     public final S next() {
         if (hasNext()) {
             currentNode = nextNode;
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/TreeWalkerImpl.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/TreeWalkerImpl.java
index cfcc10815..cfebe91d4 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/TreeWalkerImpl.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/TreeWalkerImpl.java
@@ -257,8 +257,12 @@ public final class TreeWalkerImpl implements XmlReader {
                         reader = null;
                     }
                 }
-                case STATE_CONTENT_VISITED ->
-                    handler.processCharacterData(((CoreParentNode) 
nextNode).internalGetContent(), false);
+                case STATE_CONTENT_VISITED -> {
+                    @SuppressWarnings("stringorcharacterdata")
+                    
@org.apache.axiom.core.stream.annotations.StringOrCharacterData
+                    Object data = ((CoreParentNode) 
nextNode).internalGetContent();
+                    handler.processCharacterData(data, false);
+                }
                 default -> throw new IllegalStateException();
             }
             node = nextNode;
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/BuildableContext.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/BuildableContext.java
index 8dc58a931..d460fe3b0 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/BuildableContext.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/BuildableContext.java
@@ -40,13 +40,14 @@ import org.apache.axiom.core.stream.CharacterData;
 import org.apache.axiom.core.stream.NullXmlHandler;
 import org.apache.axiom.core.stream.StreamException;
 import org.apache.axiom.core.stream.XmlHandler;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 
 final class BuildableContext extends Context implements InputContext {
     private final Context parentContext;
 
     private CoreParentNode target;
 
-    private Object pendingCharacterData;
+    private @StringOrCharacterData Object pendingCharacterData;
 
     /**
      * The {@link XmlHandler} object to send events to if pass-through is 
enabled. See {@link
@@ -260,7 +261,7 @@ final class BuildableContext extends Context implements 
InputContext {
     }
 
     @Override
-    void processCharacterData(Object data, boolean ignorable) throws 
StreamException {
+    void processCharacterData(@StringOrCharacterData Object data, boolean 
ignorable) throws StreamException {
         if (passThroughHandler != null) {
             passThroughHandler.processCharacterData(data, ignorable);
         } else if (!ignorable && pendingCharacterData == null && 
target.coreGetFirstChildIfAvailable() == null) {
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/BuilderHandler.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/BuilderHandler.java
index 2353b61e8..0eacd7772 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/BuilderHandler.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/BuilderHandler.java
@@ -29,6 +29,7 @@ import org.apache.axiom.core.DeferredParsingException;
 import org.apache.axiom.core.NodeFactory;
 import org.apache.axiom.core.stream.StreamException;
 import org.apache.axiom.core.stream.XmlHandler;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 
 final class BuilderHandler implements XmlHandler {
     final NodeFactory nodeFactory;
@@ -176,7 +177,7 @@ final class BuilderHandler implements XmlHandler {
     }
 
     @Override
-    public void processCharacterData(Object data, boolean ignorable) throws 
StreamException {
+    public void processCharacterData(@StringOrCharacterData Object data, 
boolean ignorable) throws StreamException {
         context.processCharacterData(data, ignorable);
     }
 
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/Context.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/Context.java
index 2063f841b..9efe7fe71 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/Context.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/Context.java
@@ -20,6 +20,7 @@ package org.apache.axiom.core.impl.builder;
 
 import org.apache.axiom.core.CoreParentNode;
 import org.apache.axiom.core.stream.StreamException;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 
 abstract class Context {
     protected final BuilderHandler builderHandler;
@@ -62,7 +63,7 @@ abstract class Context {
 
     abstract void attributesCompleted() throws StreamException;
 
-    abstract void processCharacterData(Object data, boolean ignorable) throws 
StreamException;
+    abstract void processCharacterData(@StringOrCharacterData Object data, 
boolean ignorable) throws StreamException;
 
     abstract Context startProcessingInstruction(String piTarget) throws 
StreamException;
 
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/UnwrappingContext.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/UnwrappingContext.java
index 668cd73e7..7d163dd03 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/UnwrappingContext.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/builder/UnwrappingContext.java
@@ -21,6 +21,7 @@ package org.apache.axiom.core.impl.builder;
 import org.apache.axiom.core.CoreNSAwareElement;
 import org.apache.axiom.core.CoreParentNode;
 import org.apache.axiom.core.stream.StreamException;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 
 final class UnwrappingContext extends Context {
     private final CoreNSAwareElement root;
@@ -87,7 +88,7 @@ final class UnwrappingContext extends Context {
     }
 
     @Override
-    void processCharacterData(Object data, boolean ignorable) throws 
StreamException {
+    void processCharacterData(@StringOrCharacterData Object data, boolean 
ignorable) throws StreamException {
         // TODO Auto-generated method stub
 
     }
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreCharacterDataContainingParentNodeMixin.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreCharacterDataContainingParentNodeMixin.java
index 717f2b43a..a7796a8bf 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreCharacterDataContainingParentNodeMixin.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreCharacterDataContainingParentNodeMixin.java
@@ -21,13 +21,14 @@ package org.apache.axiom.core.impl.mixin;
 import org.apache.axiom.core.CoreCharacterDataContainingParentNode;
 import org.apache.axiom.core.CoreModelException;
 import org.apache.axiom.core.ElementAction;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 import org.apache.axiom.weaver.annotation.Mixin;
 
 @Mixin
 public abstract class CoreCharacterDataContainingParentNodeMixin implements 
CoreCharacterDataContainingParentNode {
     @Override
-    public final Object coreGetCharacterData() throws CoreModelException {
-        Object characterData = 
internalGetCharacterData(ElementAction.RETURN_NULL);
+    public final @StringOrCharacterData Object coreGetCharacterData() throws 
CoreModelException {
+        @StringOrCharacterData Object characterData = 
internalGetCharacterData(ElementAction.RETURN_NULL);
         if (characterData == null) {
             throw new IllegalStateException();
         }
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreCharacterDataNodeMixin.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreCharacterDataNodeMixin.java
index f4fa1aebd..f0729e82c 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreCharacterDataNodeMixin.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreCharacterDataNodeMixin.java
@@ -28,12 +28,13 @@ import org.apache.axiom.core.impl.Flags;
 import org.apache.axiom.core.stream.CharacterData;
 import org.apache.axiom.core.stream.StreamException;
 import org.apache.axiom.core.stream.XmlHandler;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 import org.apache.axiom.weaver.annotation.Mixin;
 
 @Mixin
 public abstract class CoreCharacterDataNodeMixin implements 
CoreCharacterDataNode {
     /** Either a {@link String} or a {@link CharacterData} object. */
-    private Object data;
+    private @StringOrCharacterData Object data;
 
     @Override
     public final NodeType coreGetNodeType() {
@@ -51,24 +52,24 @@ public abstract class CoreCharacterDataNodeMixin implements 
CoreCharacterDataNod
     }
 
     @Override
-    public final Object coreGetCharacterData() {
+    public final @StringOrCharacterData Object coreGetCharacterData() {
         return data == null ? "" : data;
     }
 
     @Override
-    public final void coreSetCharacterData(Object data) {
+    public final void coreSetCharacterData(@StringOrCharacterData Object data) 
{
         this.data = data;
     }
 
     @Override
-    public final void coreSetCharacterData(Object data, Semantics semantics) {
+    public final void coreSetCharacterData(@StringOrCharacterData Object data, 
Semantics semantics) {
         this.data = data;
     }
 
     @Override
     public final <T> void init(ClonePolicy<T> policy, T options, CoreNode 
other) {
         CoreCharacterDataNode o = (CoreCharacterDataNode) other;
-        Object otherData = o.coreGetCharacterData();
+        @StringOrCharacterData Object otherData = o.coreGetCharacterData();
         data = otherData instanceof CloneableCharacterData 
cloneableCharacterData
                 ? cloneableCharacterData.clone(policy, options)
                 : otherData;
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
index 6077327fd..e3ebd9354 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreMixedContentContainerMixin.java
@@ -21,12 +21,14 @@ package org.apache.axiom.core.impl.mixin;
 import org.apache.axiom.core.CoreMixedContentContainer;
 import org.apache.axiom.core.CoreModelException;
 import org.apache.axiom.core.ElementAction;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 import org.apache.axiom.weaver.annotation.Mixin;
 
 @Mixin
 public abstract class CoreMixedContentContainerMixin implements 
CoreMixedContentContainer {
     @Override
-    public final Object coreGetCharacterData(ElementAction elementAction) 
throws CoreModelException {
+    public final @StringOrCharacterData Object 
coreGetCharacterData(ElementAction elementAction)
+            throws CoreModelException {
         return internalGetCharacterData(elementAction);
     }
 }
diff --git 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreParentNodeMixin.java
 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreParentNodeMixin.java
index 91aed47e5..cff323f48 100644
--- 
a/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreParentNodeMixin.java
+++ 
b/mixins/core-mixins/src/main/java/org/apache/axiom/core/impl/mixin/CoreParentNodeMixin.java
@@ -52,6 +52,7 @@ import org.apache.axiom.core.stream.CharacterData;
 import org.apache.axiom.core.stream.StreamException;
 import org.apache.axiom.core.stream.XmlHandler;
 import org.apache.axiom.core.stream.XmlReader;
+import org.apache.axiom.core.stream.annotations.StringOrCharacterData;
 import org.apache.axiom.weaver.annotation.Mixin;
 
 @Mixin
@@ -123,7 +124,10 @@ public abstract class CoreParentNodeMixin implements 
CoreParentNode {
             Content content = new Content();
             CoreCharacterDataNode cdata = 
coreGetNodeFactory().createCharacterDataNode();
             cdata.internalSetParent(this);
-            cdata.coreSetCharacterData(this.content);
+            @SuppressWarnings("stringorcharacterdata")
+            @StringOrCharacterData
+            Object characterData = this.content;
+            cdata.coreSetCharacterData(characterData);
             content.firstChild = cdata;
             content.lastChild = cdata;
             this.content = content;
@@ -342,11 +346,15 @@ public abstract class CoreParentNodeMixin implements 
CoreParentNode {
     }
 
     @Override
-    public final Object internalGetCharacterData(ElementAction elementAction) 
throws CoreModelException {
+    public final @StringOrCharacterData Object 
internalGetCharacterData(ElementAction elementAction)
+            throws CoreModelException {
         if (getState() == COMPACT) {
-            return content;
+            @SuppressWarnings("stringorcharacterdata")
+            @StringOrCharacterData
+            Object result = content;
+            return result;
         } else {
-            Object textContent = null;
+            @StringOrCharacterData Object textContent = null;
             StringBuilder buffer = null;
             int depth = 0;
             CoreChildNode child = coreGetFirstChild();
@@ -371,6 +379,7 @@ public abstract class CoreParentNodeMixin implements 
CoreParentNode {
                     }
                 } else {
                     if (child instanceof CoreCharacterDataNode || child 
instanceof CoreCDATASection) {
+                        @StringOrCharacterData
                         Object textValue = ((CoreCharacterDataContainer) 
child).coreGetCharacterData();
                         if (textValue instanceof CharacterData || ((String) 
textValue).length() != 0) {
                             if (textContent == null) {
@@ -411,7 +420,8 @@ public abstract class CoreParentNodeMixin implements 
CoreParentNode {
     }
 
     @Override
-    public final void coreSetCharacterData(Object data, Semantics semantics) 
throws CoreModelException {
+    public final void coreSetCharacterData(@StringOrCharacterData Object data, 
Semantics semantics)
+            throws CoreModelException {
         coreRemoveChildren(semantics);
         if (data != null && (data instanceof CharacterData || ((String) 
data).length() > 0)) {
             coreSetState(COMPACT);
@@ -443,6 +453,8 @@ public abstract class CoreParentNodeMixin implements 
CoreParentNode {
         CoreParentNode targetParent = (CoreParentNode) clone;
         if (policy.cloneChildren(options, coreGetNodeType()) && 
targetParent.isExpanded()) {
             if (getState() == COMPACT) {
+                @SuppressWarnings("stringorcharacterdata")
+                @StringOrCharacterData
                 Object content = this.content;
                 if (content instanceof CloneableCharacterData) {
                     content = ((CloneableCharacterData) content).clone(policy, 
options);
diff --git a/pom.xml b/pom.xml
index 33fab2de4..9ff78e095 100644
--- a/pom.xml
+++ b/pom.xml
@@ -296,6 +296,7 @@
         <mime4j.version>0.8.14</mime4j.version>
         <surefire.version>3.5.5</surefire.version>
         <jaxws-rt.version>4.0.4</jaxws-rt.version>
+        <checkerframework.version>3.48.4</checkerframework.version>
 
         <skipDeploy>false</skipDeploy>
 
@@ -557,6 +558,16 @@
                 <artifactId>guice</artifactId>
                 <version>7.0.0</version>
             </dependency>
+            <dependency>
+                <groupId>org.checkerframework</groupId>
+                <artifactId>checker-qual</artifactId>
+                <version>${checkerframework.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.checkerframework</groupId>
+                <artifactId>checker</artifactId>
+                <version>${checkerframework.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
@@ -1417,5 +1428,24 @@ $${type_declaration}</template></templates>]]></value>
                 </plugins>
             </build>
         </profile>
+        <profile>
+            <id>checkerframework</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <configuration>
+                            <annotationProcessorPaths 
combine.children="append">
+                                <annotationProcessorPath>
+                                    <groupId>${project.groupId}</groupId>
+                                    
<artifactId>stringorcharacterdata-checker</artifactId>
+                                    <version>${project.version}</version>
+                                </annotationProcessorPath>
+                            </annotationProcessorPaths>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
     </profiles>
 </project>


Reply via email to