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

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new ca02f9a35 cleanup
ca02f9a35 is described below

commit ca02f9a358113e784ff7d490fcc46d73ecddeba6
Author: Andrus Adamchik <[email protected]>
AuthorDate: Mon Jul 20 08:59:08 2026 +0200

    cleanup
---
 .../java/org/apache/cayenne/gen/StringUtils.java   |  26 +-
 .../dbsync/merge/builders/DbEntityBuilder.java     |   3 +-
 .../dbsync/merge/builders/DefaultBuilder.java      |  23 +-
 .../dbsync/merge/builders/ObjEntityBuilder.java    |   3 +-
 .../dbsync/merge/builders/ProcedureBuilder.java    |   7 +-
 .../dbimport/DefaultDbImportActionTest.java        |  29 +-
 .../compatibility/CompatibilityUpgradeService.java |   7 +-
 .../project/upgrade/DefaultUpgradeService.java     |  72 ++-
 .../project/upgrade/DefaultUpgradeServiceTest.java |   8 +-
 .../cayenne/exp/parser/PatternMatchNode.java       |  59 +-
 .../main/java/org/apache/cayenne/util/Util.java    |  36 +-
 .../java/org/apache/cayenne/util/UtilTest.java     | 656 +++++++++++----------
 12 files changed, 522 insertions(+), 407 deletions(-)

diff --git a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/StringUtils.java 
b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/StringUtils.java
index c2f86153f..d9a2277c6 100644
--- a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/StringUtils.java
+++ b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/StringUtils.java
@@ -65,7 +65,7 @@ public class StringUtils {
      * @since 1.2
      */
     public String stripClass(String aString) {
-        if (aString == null || aString.length() == 0) {
+        if (aString == null || aString.isEmpty()) {
             return aString;
         }
 
@@ -80,20 +80,30 @@ public class StringUtils {
 
     /**
      * Capitalizes the first letter of the property name.
-     * 
+     *
      * @since 1.1
      */
     public String capitalized(String name) {
-        return Util.capitalized(name);
+        if (name == null || name.isEmpty()) {
+            return name;
+        }
+
+        char c = Character.toUpperCase(name.charAt(0));
+        return (name.length() == 1) ? Character.toString(c) : c + 
name.substring(1);
     }
 
     /**
      * Returns string with lowercased first letter
-     * 
+     *
      * @since 1.2
      */
     public String uncapitalized(String aString) {
-        return Util.uncapitalized(aString);
+        if (aString == null || aString.isEmpty()) {
+            return aString;
+        }
+
+        char c = Character.toLowerCase(aString.charAt(0));
+        return (aString.length() == 1) ? Character.toString(c) : c + 
aString.substring(1);
     }
 
     /**
@@ -102,7 +112,7 @@ public class StringUtils {
      * @since 1.1
      */
     public String capitalizedAsConstant(String name) {
-        if (name == null || name.length() == 0) {
+        if (name == null || name.isEmpty()) {
             return name;
         }
 
@@ -111,7 +121,7 @@ public class StringUtils {
         // check is a good idea
         name = Util.specialCharsToJava(name);
 
-        char charArray[] = name.toCharArray();
+        char[] charArray = name.toCharArray();
         StringBuilder buffer = new StringBuilder();
 
         for (int i = 0; i < charArray.length; i++) {
@@ -144,7 +154,7 @@ public class StringUtils {
      * @since 3.1
      */
     public String pluralize(String str) {
-        if (str == null || str.length() == 0) {
+        if (str == null || str.isEmpty()) {
             return str;
         }
         else if (str.endsWith("s") || str.endsWith("x")) {
diff --git 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbEntityBuilder.java
 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbEntityBuilder.java
index e250588a5..968bec21e 100644
--- 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbEntityBuilder.java
+++ 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbEntityBuilder.java
@@ -20,7 +20,6 @@ package org.apache.cayenne.dbsync.merge.builders;
 
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.util.Util;
 
 public class DbEntityBuilder extends DefaultBuilder<DbEntity> {
 
@@ -69,7 +68,7 @@ public class DbEntityBuilder extends DefaultBuilder<DbEntity> 
{
     @Override
     public DbEntity build() {
         if (obj.getName() == null) {
-            obj.setName(Util.capitalized(getRandomJavaName()));
+            obj.setName(capitalized(getRandomJavaName()));
         }
 
         return obj;
diff --git 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DefaultBuilder.java
 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DefaultBuilder.java
index 6366ce792..6439fdb12 100644
--- 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DefaultBuilder.java
+++ 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DefaultBuilder.java
@@ -19,7 +19,6 @@
 package org.apache.cayenne.dbsync.merge.builders;
 
 import org.apache.cayenne.datafactory.DataFactory;
-import org.apache.cayenne.util.Util;
 
 public abstract class DefaultBuilder<T> implements Builder<T> {
 
@@ -35,10 +34,10 @@ public abstract class DefaultBuilder<T> implements 
Builder<T> {
         int count = dataFactory.getNumberBetween(1, 5);
         StringBuilder res = new StringBuilder();
         for (int i = 0; i < count; i++) {
-            res.append(Util.capitalized(dataFactory.getRandomWord()));
+            res.append(capitalized(dataFactory.getRandomWord()));
         }
 
-        return Util.uncapitalized(res.toString());
+        return uncapitalized(res.toString());
     }
 
     @Override
@@ -50,4 +49,22 @@ public abstract class DefaultBuilder<T> implements 
Builder<T> {
     public T random() {
         return build();
     }
+
+    protected static String uncapitalized(String s) {
+        if (s == null || s.isEmpty()) {
+            return s;
+        }
+
+        char c = Character.toLowerCase(s.charAt(0));
+        return (s.length() == 1) ? Character.toString(c) : c + s.substring(1);
+    }
+
+    protected static String capitalized(String s) {
+        if (s == null || s.isEmpty()) {
+            return s;
+        }
+
+        char c = Character.toUpperCase(s.charAt(0));
+        return (s.length() == 1) ? Character.toString(c) : c + s.substring(1);
+    }
 }
diff --git 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjEntityBuilder.java
 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjEntityBuilder.java
index 08063d48f..f059ddd15 100644
--- 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjEntityBuilder.java
+++ 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjEntityBuilder.java
@@ -20,7 +20,6 @@ package org.apache.cayenne.dbsync.merge.builders;
 
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.util.Util;
 
 public class ObjEntityBuilder extends DefaultBuilder<ObjEntity> {
 
@@ -65,7 +64,7 @@ public class ObjEntityBuilder extends 
DefaultBuilder<ObjEntity> {
     @Override
     public ObjEntity build() {
         if (obj.getName() == null) {
-            obj.setName(Util.capitalized(getRandomJavaName()));
+            obj.setName(capitalized(getRandomJavaName()));
         }
 
         return obj;
diff --git 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ProcedureBuilder.java
 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ProcedureBuilder.java
index 8b75c44f9..8f70b2742 100644
--- 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ProcedureBuilder.java
+++ 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ProcedureBuilder.java
@@ -19,11 +19,10 @@
 
 package org.apache.cayenne.dbsync.merge.builders;
 
-import java.util.Arrays;
-
 import org.apache.cayenne.map.Procedure;
 import org.apache.cayenne.map.ProcedureParameter;
-import org.apache.cayenne.util.Util;
+
+import java.util.Arrays;
 
 public class ProcedureBuilder extends DefaultBuilder<Procedure> {
 
@@ -48,7 +47,7 @@ public class ProcedureBuilder extends 
DefaultBuilder<Procedure> {
 
     public Procedure build() {
         if(obj.getName() == null) {
-            obj.setName(Util.capitalized(getRandomJavaName()));
+            obj.setName(capitalized(getRandomJavaName()));
         }
 
         return obj;
diff --git 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java
 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java
index 3d940f3a1..6cb7c7924 100644
--- 
a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java
+++ 
b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java
@@ -55,7 +55,10 @@ import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 
 import javax.sql.DataSource;
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -319,7 +322,7 @@ public class DefaultDbImportActionTest {
 
         assertTrue(out.isFile());
 
-        String contents = Util.stringFromFile(out);
+        String contents = stringFromFile(out);
         assertTrue(contents.contains("project-version=\""), "Has no project 
version saved");
     }
 
@@ -351,10 +354,10 @@ public class DefaultDbImportActionTest {
         assertTrue(dataMapFile.isFile());
         assertTrue(projectFile.isFile());
 
-        String dataMapContents = Util.stringFromFile(dataMapFile);
+        String dataMapContents = stringFromFile(dataMapFile);
         assertTrue(dataMapContents.contains("project-version=\""), "Has no 
project version saved");
 
-        String projectContents = Util.stringFromFile(projectFile);
+        String projectContents = stringFromFile(projectFile);
         assertTrue(projectContents.contains("project-version=\""), "Has no 
project version saved");
         assertTrue(projectContents.contains("<map 
name=\"testSaveLoaded2\"/>"), "Has no datamap in project");
     }
@@ -396,10 +399,10 @@ public class DefaultDbImportActionTest {
         assertTrue(dataMapFile.isFile());
         assertTrue(projectFile.isFile());
 
-        String dataMapContents = Util.stringFromFile(dataMapFile);
+        String dataMapContents = stringFromFile(dataMapFile);
         assertTrue(dataMapContents.contains("project-version=\""), "Has no 
project version saved");
 
-        String projectContents = Util.stringFromFile(projectFile);
+        String projectContents = stringFromFile(projectFile);
         assertTrue(projectContents.contains("project-version=\""), "Has no 
project version saved");
         assertTrue(projectContents.contains("<map 
name=\"testSaveLoaded3\"/>"), "Has no datamap in project");
     }
@@ -453,11 +456,11 @@ public class DefaultDbImportActionTest {
         assertTrue(dataMapFile.isFile());
         assertTrue(projectFile.isFile());
 
-        String dataMapContents = Util.stringFromFile(dataMapFile);
+        String dataMapContents = stringFromFile(dataMapFile);
         assertTrue(dataMapContents.contains("project-version=\""), "Has no 
project version saved");
         assertFalse(dataMapContents.contains("<db-entity"));
 
-        String projectContents = Util.stringFromFile(projectFile);
+        String projectContents = stringFromFile(projectFile);
         assertTrue(projectContents.contains("project-version=\""), "Has no 
project version saved");
         assertEquals(1, Util.countMatches(projectContents, "<map 
name=\"testSaveLoaded4\"/>"), "Has no or too many datamaps in project");
     }
@@ -481,4 +484,16 @@ public class DefaultDbImportActionTest {
         }
         return res;
     }
+
+    private static String stringFromFile(File file) throws IOException {
+        StringBuilder buf = new StringBuilder();
+
+        try (BufferedReader in = new BufferedReader(new FileReader(file))) {
+            String line;
+            while ((line = in.readLine()) != null) {
+                buf.append(line).append(System.lineSeparator());
+            }
+        }
+        return buf.toString();
+    }
 }
diff --git 
a/cayenne-project-compatibility/src/main/java/org/apache/cayenne/project/compatibility/CompatibilityUpgradeService.java
 
b/cayenne-project-compatibility/src/main/java/org/apache/cayenne/project/compatibility/CompatibilityUpgradeService.java
index fe9e3c5a4..d1ccb4ad7 100644
--- 
a/cayenne-project-compatibility/src/main/java/org/apache/cayenne/project/compatibility/CompatibilityUpgradeService.java
+++ 
b/cayenne-project-compatibility/src/main/java/org/apache/cayenne/project/compatibility/CompatibilityUpgradeService.java
@@ -19,17 +19,16 @@
 
 package org.apache.cayenne.project.compatibility;
 
-import java.util.List;
-
 import org.apache.cayenne.configuration.DataChannelDescriptor;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.project.upgrade.DefaultUpgradeService;
 import org.apache.cayenne.project.upgrade.UpgradeUnit;
 import org.apache.cayenne.project.upgrade.handlers.UpgradeHandler;
 import org.apache.cayenne.resource.Resource;
-import org.apache.cayenne.util.Util;
 import org.w3c.dom.Document;
 
+import java.util.List;
+
 /**
  * @since 4.1
  */
@@ -56,7 +55,7 @@ public class CompatibilityUpgradeService extends 
DefaultUpgradeService {
 
     public Resource upgradeDataMap(Resource resource) {
         List<UpgradeHandler> handlerList = 
getHandlersForVersion(loadProjectVersion(resource));
-        Document document =  Util.readDocument(resource.getURL());
+        Document document =  readDocument(resource.getURL());
         UpgradeUnit upgradeUnit = new UpgradeUnit(resource, document);
         for(UpgradeHandler handler : handlerList) {
             handler.processDataMapDom(upgradeUnit);
diff --git 
a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DefaultUpgradeService.java
 
b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DefaultUpgradeService.java
index 4c219ada3..ecaa22571 100644
--- 
a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DefaultUpgradeService.java
+++ 
b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DefaultUpgradeService.java
@@ -19,25 +19,6 @@
 
 package org.apache.cayenne.project.upgrade;
 
-import java.io.InputStream;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import javax.xml.transform.Result;
-import javax.xml.transform.Source;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathFactory;
-
 import org.apache.cayenne.ConfigurationException;
 import org.apache.cayenne.configuration.ConfigurationTree;
 import org.apache.cayenne.configuration.DataChannelDescriptor;
@@ -61,6 +42,28 @@ import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
 import org.xml.sax.helpers.DefaultHandler;
 
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
 import static org.apache.cayenne.util.Util.isBlank;
 
 /**
@@ -170,14 +173,14 @@ public class DefaultUpgradeService implements 
UpgradeService {
         List<UpgradeUnit> allUnits = new ArrayList<>();
 
         // Load DOM for all resources
-        Document projectDocument = Util.readDocument(resource.getURL());
+        Document projectDocument = readDocument(resource.getURL());
         UpgradeUnit projectUnit = new UpgradeUnit(resource, projectDocument);
         allUnits.add(projectUnit);
 
         List<Resource> dataMapResources = 
getAdditionalDatamapResources(projectUnit);
         List<UpgradeUnit> dataMapUnits = new 
ArrayList<>(dataMapResources.size());
         for (Resource dataMapResource : dataMapResources) {
-            dataMapUnits.add(new UpgradeUnit(dataMapResource, 
Util.readDocument(dataMapResource.getURL())));
+            dataMapUnits.add(new UpgradeUnit(dataMapResource, 
readDocument(dataMapResource.getURL())));
         }
         allUnits.addAll(dataMapUnits);
 
@@ -303,6 +306,33 @@ public class DefaultUpgradeService implements 
UpgradeService {
         return Double.parseDouble(buffer.toString());
     }
 
+    protected static Document readDocument(URL url) {
+        DocumentBuilderFactory documentBuilderFactory = 
DocumentBuilderFactory.newInstance();
+        documentBuilderFactory.setNamespaceAware(false);
+        documentBuilderFactory.setXIncludeAware(false);
+        documentBuilderFactory.setExpandEntityReferences(false);
+
+        try {
+            
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl";,
 true);
+            
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities";,
 false);
+            
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities";,
 false);
+            
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd";,
 false);
+        } catch (ParserConfigurationException ex) {
+            throw new ConfigurationException("Unable to configure 
DocumentBuilderFactory", ex);
+        }
+
+        try {
+            DocumentBuilder domBuilder = 
documentBuilderFactory.newDocumentBuilder();
+            try (InputStream inputStream = url.openStream()) {
+                return domBuilder.parse(inputStream);
+            } catch (IOException | SAXException e) {
+                throw new ConfigurationException("Error loading configuration 
from %s", e, url);
+            }
+        } catch (ParserConfigurationException e) {
+            throw new ConfigurationException(e);
+        }
+    }
+
     private static class VersionComparator implements Comparator<String> {
 
         private static final VersionComparator INSTANCE = new 
VersionComparator();
diff --git 
a/cayenne-project/src/test/java/org/apache/cayenne/project/upgrade/DefaultUpgradeServiceTest.java
 
b/cayenne-project/src/test/java/org/apache/cayenne/project/upgrade/DefaultUpgradeServiceTest.java
index 66687d1bb..8a006c96c 100644
--- 
a/cayenne-project/src/test/java/org/apache/cayenne/project/upgrade/DefaultUpgradeServiceTest.java
+++ 
b/cayenne-project/src/test/java/org/apache/cayenne/project/upgrade/DefaultUpgradeServiceTest.java
@@ -22,7 +22,6 @@ package org.apache.cayenne.project.upgrade;
 import org.apache.cayenne.project.upgrade.handlers.UpgradeHandler;
 import org.apache.cayenne.resource.Resource;
 import org.apache.cayenne.resource.URLResource;
-import org.apache.cayenne.util.Util;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -37,6 +36,7 @@ import java.io.InputStreamReader;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -84,14 +84,14 @@ public class DefaultUpgradeServiceTest {
 
     @Test
     public void getAdditionalDatamapResources() throws Exception {
-        URL url = getClass().getResource("../cayenne-PROJECT1.xml");
+        URL url = 
Objects.requireNonNull(getClass().getResource("../cayenne-PROJECT1.xml"));
         Resource resource = new URLResource(url);
         Document document = readDocument(url);
         UpgradeUnit unit = new UpgradeUnit(resource, document);
         List<Resource> resources = 
upgradeService.getAdditionalDatamapResources(unit);
 
         assertEquals(2, resources.size());
-        
assertTrue(resources.get(0).getURL().sameFile(getClass().getResource("../testProjectMap1_1.map.xml")));
+        
assertTrue(resources.getFirst().getURL().sameFile(getClass().getResource("../testProjectMap1_1.map.xml")));
     }
 
     @Test
@@ -136,7 +136,7 @@ public class DefaultUpgradeServiceTest {
 
     @Test
     public void readDocument() {
-        Document document = 
Util.readDocument(getClass().getResource("../cayenne-PROJECT1.xml"));
+        Document document = 
DefaultUpgradeService.readDocument(getClass().getResource("../cayenne-PROJECT1.xml"));
         assertEquals("12", 
document.getDocumentElement().getAttribute("project-version"));
     }
 
diff --git 
a/cayenne/src/main/java/org/apache/cayenne/exp/parser/PatternMatchNode.java 
b/cayenne/src/main/java/org/apache/cayenne/exp/parser/PatternMatchNode.java
index 41c977288..33c38dcd6 100644
--- a/cayenne/src/main/java/org/apache/cayenne/exp/parser/PatternMatchNode.java
+++ b/cayenne/src/main/java/org/apache/cayenne/exp/parser/PatternMatchNode.java
@@ -19,17 +19,16 @@
 
 package org.apache.cayenne.exp.parser;
 
+import org.apache.cayenne.CayenneRuntimeException;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.regex.Pattern;
 
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.util.Util;
-
 /**
  * Superclass of pattern matching nodes. Assumes that subclass is a binary 
expression with
  * the second operand being a pattern.
- * 
+ *
  * @since 1.1
  */
 public abstract class PatternMatchNode extends ConditionNode {
@@ -51,8 +50,8 @@ public abstract class PatternMatchNode extends ConditionNode {
     }
 
     SimpleNode wrap(Object pattern) {
-        if(pattern instanceof SimpleNode) {
-            return (SimpleNode)pattern;
+        if (pattern instanceof SimpleNode) {
+            return (SimpleNode) pattern;
         }
         return new ASTScalar(pattern);
     }
@@ -110,7 +109,7 @@ public abstract class PatternMatchNode extends 
ConditionNode {
 
                     // precompile pattern
                     Node node = jjtGetChild(1);
-                    if(node instanceof ASTScalar) {
+                    if (node instanceof ASTScalar) {
                         ASTScalar patternNode = (ASTScalar) node;
                         if (patternNode == null) {
                             patternCompiled = true;
@@ -123,7 +122,7 @@ public abstract class PatternMatchNode extends 
ConditionNode {
                             return null;
                         }
 
-                        pattern = Util.sqlPatternToPattern(srcPattern, 
ignoringCase);
+                        pattern = sqlPatternToPattern(srcPattern, 
ignoringCase);
                         patternCompiled = true;
                     }
                 }
@@ -147,9 +146,9 @@ public abstract class PatternMatchNode extends 
ConditionNode {
     protected void appendChildrenAsEJBQL(List<Object> parameterAccumulator, 
Appendable out, String rootId) throws IOException {
         super.appendChildrenAsEJBQL(parameterAccumulator, out, rootId);
 
-        if(0 != getEscapeChar()) {
+        if (0 != getEscapeChar()) {
 
-            if('\'' == getEscapeChar()) {
+            if ('\'' == getEscapeChar()) {
                 throw new CayenneRuntimeException("unable to escape an EJBQL 
like clause with a single quote character");
             }
 
@@ -159,4 +158,44 @@ public abstract class PatternMatchNode extends 
ConditionNode {
         }
     }
 
+    private static Pattern sqlPatternToPattern(String pattern, boolean 
ignoreCase) {
+        if (pattern == null) {
+            throw new NullPointerException("Null pattern.");
+        }
+
+        if (pattern.length() == 0) {
+            throw new IllegalArgumentException("Empty pattern.");
+        }
+
+        StringBuilder buffer = new StringBuilder();
+
+        // convert * into regex syntax
+        // e.g. abc*x becomes ^abc.*x$
+        // or abc?x becomes ^abc.?x$
+        buffer.append("^");
+        for (int j = 0; j < pattern.length(); j++) {
+            char nextChar = pattern.charAt(j);
+            if (nextChar == '%') {
+                nextChar = '*';
+            }
+
+            if (nextChar == '*' || nextChar == '?') {
+                buffer.append('.');
+            }
+            // escape special chars
+            else if (nextChar == '.'
+                    || nextChar == '/'
+                    || nextChar == '$'
+                    || nextChar == '^') {
+                buffer.append('\\');
+            }
+
+            buffer.append(nextChar);
+        }
+
+        buffer.append("$");
+
+        int flag = ignoreCase ? Pattern.CASE_INSENSITIVE | 
Pattern.UNICODE_CASE : 0;
+        return Pattern.compile(buffer.toString(), flag);
+    }
 }
diff --git a/cayenne/src/main/java/org/apache/cayenne/util/Util.java 
b/cayenne/src/main/java/org/apache/cayenne/util/Util.java
index 01e9ff22e..aa8b7a198 100644
--- a/cayenne/src/main/java/org/apache/cayenne/util/Util.java
+++ b/cayenne/src/main/java/org/apache/cayenne/util/Util.java
@@ -105,17 +105,21 @@ public class Util {
     }
 
     /**
-     * Reads file contents, returning it as a String, using System default line
-     * separator.
+     * Reads file contents, returning it as a String, using System default 
line separator.
+     *
+     * @deprecated unused
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static String stringFromFile(File file) throws IOException {
         return stringFromFile(file, System.lineSeparator());
     }
 
     /**
-     * Reads file contents, returning it as a String, joining lines with
-     * provided separator.
+     * Reads file contents, returning it as a String, joining lines with 
provided separator.
+     *
+     * @deprecated unused
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static String stringFromFile(File file, String joinWith) throws 
IOException {
         StringBuilder buf = new StringBuilder();
 
@@ -265,10 +269,8 @@ public class Util {
                 }
             }
 
-            return true;
-        } else {
-            return true;
         }
+        return true;
     }
 
     /**
@@ -311,7 +313,9 @@ public class Util {
      * Capitalizes the first letter of the property name.
      *
      * @since 4.1
+     * @deprecated unused
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static String capitalized(String name) {
         if (name == null || name.isEmpty()) {
             return name;
@@ -325,7 +329,9 @@ public class Util {
      * Returns string with lowercased first letter
      *
      * @since 4.2
+     * @deprecated unused
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static String uncapitalized(String aString) {
         if (aString == null || aString.isEmpty()) {
             return aString;
@@ -377,10 +383,10 @@ public class Util {
     }
 
     /**
-     * @param url to read
-     * @return org.w3c.dom.Document from the given URL
      * @since 4.1
+     * @deprecated unused
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static Document readDocument(URL url) {
         DocumentBuilderFactory documentBuilderFactory = 
DocumentBuilderFactory.newInstance();
         documentBuilderFactory.setNamespaceAware(false);
@@ -442,7 +448,7 @@ public class Util {
      *
      * @since 1.2
      * @deprecated use {@link java.util.stream.IntStream#range} with
-     *             {@link java.util.stream.Collectors#toMap} to build a map 
from parallel arrays
+     * {@link java.util.stream.Collectors#toMap} to build a map from parallel 
arrays
      */
     @Deprecated(since = "5.0", forRemoval = true)
     public static <K, V> Map<K, V> toMap(K[] keys, V[] values) {
@@ -567,11 +573,9 @@ public class Util {
     /**
      * Trims long strings substituting middle part with "...".
      *
-     * @param str       String to trim.
-     * @param maxLength maximum allowable length. Must be at least 5, or an
-     *                  IllegalArgumentException is thrown.
-     * @return String
+     * @deprecated unused
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static String prettyTrim(String str, int maxLength) {
         if (maxLength < 5) {
             throw new IllegalArgumentException("Algorithm for 'prettyTrim' 
works only with length >= 5. "
@@ -624,7 +628,9 @@ public class Util {
 
     /**
      * @since 1.2
+     * @deprecated unused
      */
+    @Deprecated(since = "5.0", forRemoval = true)
     public static Pattern sqlPatternToPattern(String pattern, boolean 
ignoreCase) {
         String preprocessed = RegexUtil.sqlPatternToRegex(pattern);
 
@@ -710,7 +716,7 @@ public class Util {
             }
 
             if (len > 1) {
-                buf.append(token.substring(1, len));
+                buf.append(token, 1, len);
             }
         }
         return buf.toString();
diff --git a/cayenne/src/test/java/org/apache/cayenne/util/UtilTest.java 
b/cayenne/src/test/java/org/apache/cayenne/util/UtilTest.java
index 99010cb4e..9761b7d75 100644
--- a/cayenne/src/test/java/org/apache/cayenne/util/UtilTest.java
+++ b/cayenne/src/test/java/org/apache/cayenne/util/UtilTest.java
@@ -38,331 +38,333 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class UtilTest {
 
-       private File fTmpFileInCurrentDir;
-       private String fTmpFileName;
-       private File fTmpFileCopy;
-
-       @BeforeEach
-       public void setUp() throws Exception {
-               fTmpFileName = "." + File.separator + 
System.currentTimeMillis() + ".tmp";
-
-               fTmpFileInCurrentDir = new File(fTmpFileName);
-
-               // right some garbage to the temp file, so that it is not empty
-               try (FileWriter fout = new FileWriter(fTmpFileInCurrentDir)) {
-                       fout.write("This is total garbage..");
-               }
-
-               fTmpFileCopy = new File(fTmpFileName + ".copy");
-       }
-
-       @AfterEach
-       public void tearDown() throws Exception {
-               if (!fTmpFileInCurrentDir.delete())
-                       throw new Exception("Error deleting temporary file: " + 
fTmpFileInCurrentDir);
-
-               if (fTmpFileCopy.exists() && !fTmpFileCopy.delete())
-                       throw new Exception("Error deleting temporary file: " + 
fTmpFileCopy);
-
-       }
-
-       @Test
-       @SuppressWarnings("deprecation")
-       public void getJavaClass() throws Exception {
-               assertEquals(byte.class.getName(), 
Util.getJavaClass("byte").getName());
-               assertEquals(byte[].class.getName(), 
Util.getJavaClass("byte[]").getName());
-               assertEquals(String[].class.getName(), 
Util.getJavaClass("java.lang.String[]").getName());
-               assertEquals(UtilTest[].class.getName(), 
Util.getJavaClass(getClass().getName() + "[]").getName());
-       }
-
-       @Test
-       @SuppressWarnings("deprecation")
-       public void toMap() {
-               Object[] keys = new Object[] { "a", "b" };
-               Object[] values = new Object[] { "1", "2" };
-
-               Map<Object, Object> map = Util.toMap(keys, values);
-               assertEquals(2, map.size());
-               assertEquals("1", map.get("a"));
-               assertEquals("2", map.get("b"));
-
-               // check that map is mutable
-               map.put("c", "3");
-
-               // check that two null maps work
-               Map<Object, Object> emptyMap = Util.toMap(null, new Object[0]);
-               assertTrue(emptyMap.isEmpty());
-               emptyMap.put("key1", "value1");
-
-               // check arrays with different sizes
-               Object[] values2 = new Object[] { "1" };
-               assertThrows(IllegalArgumentException.class, () -> 
Util.toMap(keys, values2));
-       }
-
-       @Test
-       public void stripLineBreaks() {
-
-               // no breaks
-               assertEquals("PnMusdkams34 H AnYtk M", 
Util.stripLineBreaks("PnMusdkams34 H AnYtk M", 'A'));
-
-               // Windows
-               assertEquals("TyusdsdsdQaAbAc", 
Util.stripLineBreaks("TyusdsdsdQa\r\nb\r\nc", 'A'));
-
-               // Mac
-               assertEquals("aBbBc", Util.stripLineBreaks("a\rb\rc", 'B'));
-
-               // UNIX
-               assertEquals("aCbCc", Util.stripLineBreaks("a\nb\nc", 'C'));
-       }
-
-       @Test
-       public void cloneViaSerialization() throws Exception {
-               // need a special subclass of Object to make "clone" method 
public
-               MockSerializable o1 = new MockSerializable();
-               Object o2 = Util.cloneViaSerialization(o1);
-               assertEquals(o1, o2);
-               assertTrue(o1 != o2);
-       }
-
-       @Test
-       public void packagePath1() throws Exception {
-               String expectedPath = "org/apache/cayenne/util";
-               assertEquals(expectedPath, 
Util.getPackagePath(UtilTest.class.getName()));
-       }
-
-       @Test
-       public void packagePath2() throws Exception {
-               // inner class
-               class TmpTest extends Object {
-               }
-
-               String expectedPath = "org/apache/cayenne/util";
-               assertEquals(expectedPath, 
Util.getPackagePath(TmpTest.class.getName()));
-       }
-
-       @Test
-       public void packagePath3() throws Exception {
-               assertEquals("", Util.getPackagePath("ClassWithNoPackage"));
-       }
-
-       @Test
-       public void isEmptyString1() throws Exception {
-               assertTrue(Util.isEmptyString(""));
-       }
-
-       @Test
-       public void isEmptyString2() throws Exception {
-               assertFalse(Util.isEmptyString("  "));
-       }
-
-       @Test
-       public void isEmptyString3() throws Exception {
-               assertTrue(Util.isEmptyString(null));
-       }
-
-       @Test
-       public void backslashFix() throws Exception {
-               String strBefore = "abcd\\12345\\";
-               String strAfter = "abcd/12345/";
-               assertEquals(strAfter, Util.substBackslashes(strBefore));
-       }
-
-       @Test
-       @SuppressWarnings("deprecation")
-       public void nullSafeEquals() throws Exception {
-               // need a special subclass of Object to make "clone" method 
public
-               class CloneableObject implements Cloneable {
-
-                       @Override
-                       public Object clone() throws CloneNotSupportedException 
{
-                               return super.clone();
-                       }
-
-                       @Override
-                       public boolean equals(Object obj) {
-                               if (obj == null)
-                                       return false;
-
-                               // for the purpose of this test
-                               // all objects of this class considered equal
-                               // (since they carry no state)
-                               return obj.getClass() == this.getClass();
-                       }
-               }
-
-               CloneableObject o1 = new CloneableObject();
-               Object o2 = new Object();
-               Object o3 = o1.clone();
-
-               assertTrue(o3.equals(o1));
-               assertTrue(Util.nullSafeEquals(o1, o1));
-               assertFalse(Util.nullSafeEquals(o1, o2));
-               assertTrue(Util.nullSafeEquals(o1, o3));
-               assertFalse(Util.nullSafeEquals(o1, null));
-               assertFalse(Util.nullSafeEquals(null, o1));
-               assertTrue(Util.nullSafeEquals(null, null));
-       }
-
-       @Test
-       public void extractFileExtension1() throws Exception {
-               String fullName = "n.ext";
-               assertEquals("ext", Util.extractFileExtension(fullName));
-       }
-
-       @Test
-       public void extractFileExtension2() throws Exception {
-               String fullName = "n";
-               assertNull(Util.extractFileExtension(fullName));
-       }
-
-       @Test
-       public void extractFileExtension3() throws Exception {
-               String fullName = ".ext";
-               assertNull(Util.extractFileExtension(fullName));
-       }
-
-       @Test
-       public void stripFileExtension1() throws Exception {
-               String fullName = "n.ext";
-               assertEquals("n", Util.stripFileExtension(fullName));
-       }
-
-       @Test
-       public void stripFileExtension2() throws Exception {
-               String fullName = "n";
-               assertEquals("n", Util.stripFileExtension(fullName));
-       }
-
-       @Test
-       public void stripFileExtension3() throws Exception {
-               String fullName = ".ext";
-               assertEquals(".ext", Util.stripFileExtension(fullName));
-       }
-
-       @Test
-       public void encodeXmlAttribute1() throws Exception {
-               String unencoded = "normalstring";
-               assertEquals(unencoded, Util.encodeXmlAttribute(unencoded));
-       }
-
-       @Test
-       public void encodeXmlAttribute2() throws Exception {
-               String unencoded = "<a>";
-               assertEquals("&lt;a&gt;", Util.encodeXmlAttribute(unencoded));
-       }
-
-       @Test
-       public void encodeXmlAttribute3() throws Exception {
-               String unencoded = "a&b";
-               assertEquals("a&amp;b", Util.encodeXmlAttribute(unencoded));
-       }
-
-       @Test
-       public void unwindException1() throws Exception {
-               Throwable e = new Throwable();
-               assertSame(e, Util.unwindException(e));
-       }
-
-       @Test
-       public void unwindException2() throws Exception {
-               CayenneRuntimeException e = new CayenneRuntimeException();
-               assertSame(e, Util.unwindException(e));
-       }
-
-       @Test
-       public void unwindException3() throws Exception {
-               Throwable root = new Throwable();
-               CayenneRuntimeException e = new CayenneRuntimeException(root);
-               assertSame(root, Util.unwindException(e));
-       }
-
-       @Test
-       public void prettyTrim1() throws Exception {
-               // size is too short, must throw
-               assertThrows(IllegalArgumentException.class, () -> 
Util.prettyTrim("abc", 4));
-       }
-
-       @Test
-       public void prettyTrim2() throws Exception {
-               assertEquals("123", Util.prettyTrim("123", 6));
-               assertEquals("123456", Util.prettyTrim("123456", 6));
-               assertEquals("1...67", Util.prettyTrim("1234567", 6));
-               assertEquals("1...78", Util.prettyTrim("12345678", 6));
-       }
-
-       @Test
-       public void underscoredToJava1() throws Exception {
-               String expected = "ClassNameIdentifier";
-               assertEquals(expected, Util.underscoredToJava(
-                               "_CLASS_NAME_IDENTIFIER_",
-                               true));
-       }
-
-       @Test
-       public void underscoredToJava2() throws Exception {
-               String expected = "propNameIdentifier123";
-               assertEquals(expected, Util.underscoredToJava(
-                               "_prop_name_Identifier_123",
-                               false));
-       }
-
-       @Test
-       public void underscoredToJava3() throws Exception {
-               String expected = "lastName";
-               assertEquals(expected, Util.underscoredToJava("lastName", 
false));
-       }
-
-       @Test
-       public void underscoredToJava4() throws Exception {
-               String expected = "lastName";
-               assertEquals(expected, Util.underscoredToJava("LastName", 
false));
-       }
-
-       @Test
-       public void underscoredToJava5() throws Exception {
-               String expected = "LastName";
-               assertEquals(expected, Util.underscoredToJava("LastName", 
true));
-       }
-
-       @Test
-       public void underscoredToJavaSpecialChars() throws Exception {
-               assertEquals("ABCpoundXyz", Util.underscoredToJava("ABC#_XYZ", 
true));
-       }
-
-       @Test
-       public void unwindException() {
-               SQLException sql = new SQLException("bad sql");
-               CayenneRuntimeException wrapper = new 
CayenneRuntimeException(sql);
-
-               // the plain form unwinds all the way to the non-Cayenne root
-               assertSame(sql, Util.unwindException(wrapper));
-       }
-
-       @Test
-       public void unwindExceptionUpTo_stopsAtWrapper() {
-               SQLException sql = new SQLException("bad sql");
-               CayenneRuntimeException wrapper = new 
CayenneRuntimeException(sql);
-
-               // does not unwind past the CayenneRuntimeException into its 
lower-level SQLException cause
-               assertSame(wrapper, Util.unwindException(wrapper, 
CayenneRuntimeException.class));
-       }
-
-       @Test
-       public void unwindExceptionUpTo_returnsInnermostMatch() {
-               SQLException sql = new SQLException("bad sql");
-               CayenneRuntimeException inner = new 
CayenneRuntimeException("inner", sql);
-               CayenneRuntimeException outer = new 
CayenneRuntimeException("outer", inner);
-
-               // generic outer wrapper is stripped, but unwinding stops at 
the innermost matching exception
-               assertSame(inner, Util.unwindException(outer, 
CayenneRuntimeException.class));
-       }
-
-       @Test
-       public void unwindExceptionUpTo_noMatchUnwindsToRoot() {
-               IllegalArgumentException root = new 
IllegalArgumentException("root");
-               RuntimeException wrapper = new RuntimeException(root);
-
-               // no exception of the requested type in the chain, so behaves 
like the plain unwind
-               assertSame(root, Util.unwindException(wrapper, 
SQLException.class));
-       }
+    private File fTmpFileInCurrentDir;
+    private String fTmpFileName;
+    private File fTmpFileCopy;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        fTmpFileName = "." + File.separator + System.currentTimeMillis() + 
".tmp";
+
+        fTmpFileInCurrentDir = new File(fTmpFileName);
+
+        // right some garbage to the temp file, so that it is not empty
+        try (FileWriter fout = new FileWriter(fTmpFileInCurrentDir)) {
+            fout.write("This is total garbage..");
+        }
+
+        fTmpFileCopy = new File(fTmpFileName + ".copy");
+    }
+
+    @AfterEach
+    public void tearDown() throws Exception {
+        if (!fTmpFileInCurrentDir.delete())
+            throw new Exception("Error deleting temporary file: " + 
fTmpFileInCurrentDir);
+
+        if (fTmpFileCopy.exists() && !fTmpFileCopy.delete())
+            throw new Exception("Error deleting temporary file: " + 
fTmpFileCopy);
+
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void getJavaClass() throws Exception {
+        assertEquals(byte.class.getName(), 
Util.getJavaClass("byte").getName());
+        assertEquals(byte[].class.getName(), 
Util.getJavaClass("byte[]").getName());
+        assertEquals(String[].class.getName(), 
Util.getJavaClass("java.lang.String[]").getName());
+        assertEquals(UtilTest[].class.getName(), 
Util.getJavaClass(getClass().getName() + "[]").getName());
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void toMap() {
+        Object[] keys = new Object[]{"a", "b"};
+        Object[] values = new Object[]{"1", "2"};
+
+        Map<Object, Object> map = Util.toMap(keys, values);
+        assertEquals(2, map.size());
+        assertEquals("1", map.get("a"));
+        assertEquals("2", map.get("b"));
+
+        // check that map is mutable
+        map.put("c", "3");
+
+        // check that two null maps work
+        Map<Object, Object> emptyMap = Util.toMap(null, new Object[0]);
+        assertTrue(emptyMap.isEmpty());
+        emptyMap.put("key1", "value1");
+
+        // check arrays with different sizes
+        Object[] values2 = new Object[]{"1"};
+        assertThrows(IllegalArgumentException.class, () -> Util.toMap(keys, 
values2));
+    }
+
+    @Test
+    public void stripLineBreaks() {
+
+        // no breaks
+        assertEquals("PnMusdkams34 H AnYtk M", 
Util.stripLineBreaks("PnMusdkams34 H AnYtk M", 'A'));
+
+        // Windows
+        assertEquals("TyusdsdsdQaAbAc", 
Util.stripLineBreaks("TyusdsdsdQa\r\nb\r\nc", 'A'));
+
+        // Mac
+        assertEquals("aBbBc", Util.stripLineBreaks("a\rb\rc", 'B'));
+
+        // UNIX
+        assertEquals("aCbCc", Util.stripLineBreaks("a\nb\nc", 'C'));
+    }
+
+    @Test
+    public void cloneViaSerialization() throws Exception {
+        // need a special subclass of Object to make "clone" method public
+        MockSerializable o1 = new MockSerializable();
+        Object o2 = Util.cloneViaSerialization(o1);
+        assertEquals(o1, o2);
+        assertTrue(o1 != o2);
+    }
+
+    @Test
+    public void packagePath1() throws Exception {
+        String expectedPath = "org/apache/cayenne/util";
+        assertEquals(expectedPath, 
Util.getPackagePath(UtilTest.class.getName()));
+    }
+
+    @Test
+    public void packagePath2() throws Exception {
+        // inner class
+        class TmpTest extends Object {
+        }
+
+        String expectedPath = "org/apache/cayenne/util";
+        assertEquals(expectedPath, 
Util.getPackagePath(TmpTest.class.getName()));
+    }
+
+    @Test
+    public void packagePath3() throws Exception {
+        assertEquals("", Util.getPackagePath("ClassWithNoPackage"));
+    }
+
+    @Test
+    public void isEmptyString1() throws Exception {
+        assertTrue(Util.isEmptyString(""));
+    }
+
+    @Test
+    public void isEmptyString2() throws Exception {
+        assertFalse(Util.isEmptyString("  "));
+    }
+
+    @Test
+    public void isEmptyString3() throws Exception {
+        assertTrue(Util.isEmptyString(null));
+    }
+
+    @Test
+    public void backslashFix() throws Exception {
+        String strBefore = "abcd\\12345\\";
+        String strAfter = "abcd/12345/";
+        assertEquals(strAfter, Util.substBackslashes(strBefore));
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void nullSafeEquals() throws Exception {
+        // need a special subclass of Object to make "clone" method public
+        class CloneableObject implements Cloneable {
+
+            @Override
+            public Object clone() throws CloneNotSupportedException {
+                return super.clone();
+            }
+
+            @Override
+            public boolean equals(Object obj) {
+                if (obj == null)
+                    return false;
+
+                // for the purpose of this test
+                // all objects of this class considered equal
+                // (since they carry no state)
+                return obj.getClass() == this.getClass();
+            }
+        }
+
+        CloneableObject o1 = new CloneableObject();
+        Object o2 = new Object();
+        Object o3 = o1.clone();
+
+        assertTrue(o3.equals(o1));
+        assertTrue(Util.nullSafeEquals(o1, o1));
+        assertFalse(Util.nullSafeEquals(o1, o2));
+        assertTrue(Util.nullSafeEquals(o1, o3));
+        assertFalse(Util.nullSafeEquals(o1, null));
+        assertFalse(Util.nullSafeEquals(null, o1));
+        assertTrue(Util.nullSafeEquals(null, null));
+    }
+
+    @Test
+    public void extractFileExtension1() throws Exception {
+        String fullName = "n.ext";
+        assertEquals("ext", Util.extractFileExtension(fullName));
+    }
+
+    @Test
+    public void extractFileExtension2() throws Exception {
+        String fullName = "n";
+        assertNull(Util.extractFileExtension(fullName));
+    }
+
+    @Test
+    public void extractFileExtension3() throws Exception {
+        String fullName = ".ext";
+        assertNull(Util.extractFileExtension(fullName));
+    }
+
+    @Test
+    public void stripFileExtension1() throws Exception {
+        String fullName = "n.ext";
+        assertEquals("n", Util.stripFileExtension(fullName));
+    }
+
+    @Test
+    public void stripFileExtension2() throws Exception {
+        String fullName = "n";
+        assertEquals("n", Util.stripFileExtension(fullName));
+    }
+
+    @Test
+    public void stripFileExtension3() throws Exception {
+        String fullName = ".ext";
+        assertEquals(".ext", Util.stripFileExtension(fullName));
+    }
+
+    @Test
+    public void encodeXmlAttribute1() throws Exception {
+        String unencoded = "normalstring";
+        assertEquals(unencoded, Util.encodeXmlAttribute(unencoded));
+    }
+
+    @Test
+    public void encodeXmlAttribute2() throws Exception {
+        String unencoded = "<a>";
+        assertEquals("&lt;a&gt;", Util.encodeXmlAttribute(unencoded));
+    }
+
+    @Test
+    public void encodeXmlAttribute3() throws Exception {
+        String unencoded = "a&b";
+        assertEquals("a&amp;b", Util.encodeXmlAttribute(unencoded));
+    }
+
+    @Test
+    public void unwindException1() throws Exception {
+        Throwable e = new Throwable();
+        assertSame(e, Util.unwindException(e));
+    }
+
+    @Test
+    public void unwindException2() throws Exception {
+        CayenneRuntimeException e = new CayenneRuntimeException();
+        assertSame(e, Util.unwindException(e));
+    }
+
+    @Test
+    public void unwindException3() throws Exception {
+        Throwable root = new Throwable();
+        CayenneRuntimeException e = new CayenneRuntimeException(root);
+        assertSame(root, Util.unwindException(e));
+    }
+
+    @Deprecated
+    @Test
+    public void prettyTrim1() {
+        // size is too short, must throw
+        assertThrows(IllegalArgumentException.class, () -> 
Util.prettyTrim("abc", 4));
+    }
+
+    @Deprecated
+    @Test
+    public void prettyTrim2() {
+        assertEquals("123", Util.prettyTrim("123", 6));
+        assertEquals("123456", Util.prettyTrim("123456", 6));
+        assertEquals("1...67", Util.prettyTrim("1234567", 6));
+        assertEquals("1...78", Util.prettyTrim("12345678", 6));
+    }
+
+    @Test
+    public void underscoredToJava1() throws Exception {
+        String expected = "ClassNameIdentifier";
+        assertEquals(expected, Util.underscoredToJava(
+                "_CLASS_NAME_IDENTIFIER_",
+                true));
+    }
+
+    @Test
+    public void underscoredToJava2() throws Exception {
+        String expected = "propNameIdentifier123";
+        assertEquals(expected, Util.underscoredToJava(
+                "_prop_name_Identifier_123",
+                false));
+    }
+
+    @Test
+    public void underscoredToJava3() throws Exception {
+        String expected = "lastName";
+        assertEquals(expected, Util.underscoredToJava("lastName", false));
+    }
+
+    @Test
+    public void underscoredToJava4() throws Exception {
+        String expected = "lastName";
+        assertEquals(expected, Util.underscoredToJava("LastName", false));
+    }
+
+    @Test
+    public void underscoredToJava5() throws Exception {
+        String expected = "LastName";
+        assertEquals(expected, Util.underscoredToJava("LastName", true));
+    }
+
+    @Test
+    public void underscoredToJavaSpecialChars() throws Exception {
+        assertEquals("ABCpoundXyz", Util.underscoredToJava("ABC#_XYZ", true));
+    }
+
+    @Test
+    public void unwindException() {
+        SQLException sql = new SQLException("bad sql");
+        CayenneRuntimeException wrapper = new CayenneRuntimeException(sql);
+
+        // the plain form unwinds all the way to the non-Cayenne root
+        assertSame(sql, Util.unwindException(wrapper));
+    }
+
+    @Test
+    public void unwindExceptionUpTo_stopsAtWrapper() {
+        SQLException sql = new SQLException("bad sql");
+        CayenneRuntimeException wrapper = new CayenneRuntimeException(sql);
+
+        // does not unwind past the CayenneRuntimeException into its 
lower-level SQLException cause
+        assertSame(wrapper, Util.unwindException(wrapper, 
CayenneRuntimeException.class));
+    }
+
+    @Test
+    public void unwindExceptionUpTo_returnsInnermostMatch() {
+        SQLException sql = new SQLException("bad sql");
+        CayenneRuntimeException inner = new CayenneRuntimeException("inner", 
sql);
+        CayenneRuntimeException outer = new CayenneRuntimeException("outer", 
inner);
+
+        // generic outer wrapper is stripped, but unwinding stops at the 
innermost matching exception
+        assertSame(inner, Util.unwindException(outer, 
CayenneRuntimeException.class));
+    }
+
+    @Test
+    public void unwindExceptionUpTo_noMatchUnwindsToRoot() {
+        IllegalArgumentException root = new IllegalArgumentException("root");
+        RuntimeException wrapper = new RuntimeException(root);
+
+        // no exception of the requested type in the chain, so behaves like 
the plain unwind
+        assertSame(root, Util.unwindException(wrapper, SQLException.class));
+    }
 }

Reply via email to