Repository: joelittlejohn/jsonschema2pojo
Pull request #1063 (2019-12-31 17:06:48)

Restrict JSR-303 annotations to supported field types
I found that JSR-303 annotations were getting applied to types that didn't support them. For example, if a "pattern" was specified on a "string" property that also had a "format" of "uuid", then the generated field type is `UUID` class (rather than `String`) and `@Pattern` was still applied. Because that annotation isn't valid on a `UUID` type, then validators generate errors.

Continuing with this example, because a schema may be used to generate source classes in non-Java languages, there is still value to keep the "pattern" restriction on the source json schema itself for languages that might not have an in-built UUID type. 

This patch updates the various JSR-303 Rules classes to enhance them to check the generated class type of potentially annotated fields against the classes/types that are documented to support the relevant annotation (as documented in each annotation's JavaDoc).

In a second logical commit, Unit tests were added for thes JSR-303 rules classes. This required updating Mockito to a newer version that (properly) supports mocking final classes.

In a third logical commit, integration tests were updated with schemas that generate unsupported field types for the various cases, in addition to the supported types.


Patch:
diff --git a/jsonschema2pojo-cli/pom.xml b/jsonschema2pojo-cli/pom.xml
index e13ed805..635b4dff 100644
--- a/jsonschema2pojo-cli/pom.xml
+++ b/jsonschema2pojo-cli/pom.xml
@@ -130,7 +130,7 @@
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
+            <artifactId>mockito-core</artifactId>
         </dependency>
     </dependencies>
 </project>
diff --git a/jsonschema2pojo-core/pom.xml b/jsonschema2pojo-core/pom.xml
index f82ffc40..dee45e9e 100644
--- a/jsonschema2pojo-core/pom.xml
+++ b/jsonschema2pojo-core/pom.xml
@@ -76,7 +76,7 @@
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
+            <artifactId>mockito-core</artifactId>
         </dependency>
         <dependency>
             <groupId>com.github.tomakehurst</groupId>
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractRuleLogger.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractRuleLogger.java
index 5af83873..22147f6a 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractRuleLogger.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractRuleLogger.java
@@ -1,3 +1,19 @@
+/**
+ * Copyright © 2010-2017 Nokia
+ *
+ * Licensed 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.jsonschema2pojo;
 
 public abstract class AbstractRuleLogger implements RuleLogger {
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/DigitsRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/DigitsRule.java
index db212959..d0614af6 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/DigitsRule.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/DigitsRule.java
@@ -22,7 +22,6 @@ import com.sun.codemodel.JFieldVar;
 import org.jsonschema2pojo.Schema;
 
 import javax.validation.constraints.Digits;
-import java.util.NoSuchElementException;
 
 public class DigitsRule implements Rule<JFieldVar, JFieldVar> {
 
@@ -36,7 +35,8 @@ public class DigitsRule implements Rule<JFieldVar, JFieldVar> {
     public JFieldVar apply(String nodeName, JsonNode node, JsonNode parent, JFieldVar field, Schema currentSchema) {
 
         if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()
-            && node.has("integerDigits") && node.has("fractionalDigits")) {
+            && node.has("integerDigits") && node.has("fractionalDigits")
+            && isApplicableType(field)) {
 
             JAnnotationUse annotation = field.annotate(Digits.class);
 
@@ -47,4 +47,16 @@ public class DigitsRule implements Rule<JFieldVar, JFieldVar> {
         return field;
     }
 
+    private boolean isApplicableType(JFieldVar field) {
+        try {
+            Class<?> fieldClass = Class.forName(field.type().boxify().fullName());
+            // Support Strings and most number types except Double and Float, per docs on Digits annotations
+            return String.class.isAssignableFrom(fieldClass) ||
+                    (Number.class.isAssignableFrom(fieldClass) &&
+                            !Float.class.isAssignableFrom(fieldClass) && !Double.class.isAssignableFrom(fieldClass));
+        } catch (ClassNotFoundException ignore) {
+            return false;
+        }
+    }
+
 }
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRule.java
index a297e72e..0294f5e4 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRule.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRule.java
@@ -22,7 +22,10 @@ import com.fasterxml.jackson.databind.JsonNode;
 import org.jsonschema2pojo.Schema;
 import com.sun.codemodel.JAnnotationUse;
 import com.sun.codemodel.JFieldVar;
-import scala.annotation.meta.field;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
 
 public class MinItemsMaxItemsRule implements Rule<JFieldVar, JFieldVar> {
 
@@ -36,7 +39,8 @@ public class MinItemsMaxItemsRule implements Rule<JFieldVar, JFieldVar> {
     public JFieldVar apply(String nodeName, JsonNode node, JsonNode parent, JFieldVar field, Schema currentSchema) {
 
         if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()
-                && (node.has("minItems") || node.has("maxItems"))) {
+                && (node.has("minItems") || node.has("maxItems"))
+                && isApplicableType(field)) {
 
             JAnnotationUse annotation = field.annotate(Size.class);
 
@@ -52,4 +56,25 @@ public class MinItemsMaxItemsRule implements Rule<JFieldVar, JFieldVar> {
         return field;
     }
 
+    private boolean isApplicableType(JFieldVar field) {
+        try {
+            String typeName = field.type().boxify().fullName();
+            // For collections, the full name will be something like 'java.util.List<String>' and we
+            // need just 'java.util.List'.
+            int genericsPos = typeName.indexOf('<');
+            if (genericsPos > -1) {
+                typeName = typeName.substring(0, genericsPos);
+            }
+
+            Class<?> fieldClass = Class.forName(typeName);
+            return String.class.isAssignableFrom(fieldClass)
+                    || Collection.class.isAssignableFrom(fieldClass)
+                    || Map.class.isAssignableFrom(fieldClass)
+                    || Array.class.isAssignableFrom(fieldClass)
+                    || field.type().isArray();
+        } catch (ClassNotFoundException ignore) {
+            return false;
+        }
+    }
+
 }
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRule.java
index 4de9176e..7ad2a7cf 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRule.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRule.java
@@ -22,7 +22,10 @@ import com.fasterxml.jackson.databind.JsonNode;
 import org.jsonschema2pojo.Schema;
 import com.sun.codemodel.JAnnotationUse;
 import com.sun.codemodel.JFieldVar;
-import scala.annotation.meta.field;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
 
 public class MinLengthMaxLengthRule implements Rule<JFieldVar, JFieldVar> {
     
@@ -36,7 +39,8 @@ public class MinLengthMaxLengthRule implements Rule<JFieldVar, JFieldVar> {
     public JFieldVar apply(String nodeName, JsonNode node, JsonNode parent, JFieldVar field, Schema currentSchema) {
         
         if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()
-                && (node.has("minLength") || node.has("maxLength"))) {
+                && (node.has("minLength") || node.has("maxLength"))
+                && isApplicableType(field)) {
 
             JAnnotationUse annotation = field.annotate(Size.class);
 
@@ -51,5 +55,26 @@ public class MinLengthMaxLengthRule implements Rule<JFieldVar, JFieldVar> {
 
         return field;
     }
-    
+
+    private boolean isApplicableType(JFieldVar field) {
+        try {
+            String typeName = field.type().boxify().fullName();
+            // For collections, the full name will be something like 'java.util.List<String>' and we
+            // need just 'java.util.List'.
+            int genericsPos = typeName.indexOf('<');
+            if (genericsPos > -1) {
+                typeName = typeName.substring(0, genericsPos);
+            }
+
+            Class<?> fieldClass = Class.forName(typeName);
+            return String.class.isAssignableFrom(fieldClass)
+                    || Collection.class.isAssignableFrom(fieldClass)
+                    || Map.class.isAssignableFrom(fieldClass)
+                    || Array.class.isAssignableFrom(fieldClass)
+                    || field.type().isArray();
+        } catch (ClassNotFoundException ignore) {
+            return false;
+        }
+    }
+
 }
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinimumMaximumRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinimumMaximumRule.java
index a338dfa5..730113e7 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinimumMaximumRule.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/MinimumMaximumRule.java
@@ -23,7 +23,6 @@ import com.fasterxml.jackson.databind.JsonNode;
 import org.jsonschema2pojo.Schema;
 import com.sun.codemodel.JAnnotationUse;
 import com.sun.codemodel.JFieldVar;
-import scala.annotation.meta.field;
 
 public class MinimumMaximumRule implements Rule<JFieldVar, JFieldVar> {
 
@@ -36,7 +35,7 @@ public class MinimumMaximumRule implements Rule<JFieldVar, JFieldVar> {
     @Override
     public JFieldVar apply(String nodeName, JsonNode node, JsonNode parent, JFieldVar field, Schema currentSchema) {
 
-        if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()) {
+        if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() && isApplicableType(field)) {
 
             if (node.has("minimum")) {
                 JAnnotationUse annotation = field.annotate(DecimalMin.class);
@@ -53,4 +52,16 @@ public class MinimumMaximumRule implements Rule<JFieldVar, JFieldVar> {
         return field;
     }
 
+    private boolean isApplicableType(JFieldVar field) {
+        try {
+            Class<?> fieldClass = Class.forName(field.type().boxify().fullName());
+            // Support Strings and most number types except Double and Float, per docs on DecimalMax/Min annotations
+            return String.class.isAssignableFrom(fieldClass) ||
+                    (Number.class.isAssignableFrom(fieldClass) &&
+                            !Float.class.isAssignableFrom(fieldClass) && !Double.class.isAssignableFrom(fieldClass));
+        } catch (ClassNotFoundException ignore) {
+            return false;
+        }
+    }
+
 }
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PatternRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PatternRule.java
index 2bbd39f9..254e9caf 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PatternRule.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PatternRule.java
@@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.JsonNode;
 import org.jsonschema2pojo.Schema;
 import com.sun.codemodel.JAnnotationUse;
 import com.sun.codemodel.JFieldVar;
-import scala.annotation.meta.field;
 
 public class PatternRule implements Rule<JFieldVar, JFieldVar> {
 
@@ -35,7 +34,7 @@ public class PatternRule implements Rule<JFieldVar, JFieldVar> {
     @Override
     public JFieldVar apply(String nodeName, JsonNode node, JsonNode parent, JFieldVar field, Schema currentSchema) {
 
-        if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()) {
+        if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() && isApplicableType(field)) {
             JAnnotationUse annotation = field.annotate(Pattern.class);
             annotation.param("regexp", node.asText());
         }
@@ -43,4 +42,13 @@ public class PatternRule implements Rule<JFieldVar, JFieldVar> {
         return field;
     }
 
+    private boolean isApplicableType(JFieldVar field) {
+        try {
+            Class<?> fieldClass = Class.forName(field.type().boxify().fullName());
+            return String.class.isAssignableFrom(fieldClass);
+        } catch (ClassNotFoundException ignore) {
+            return false;
+        }
+    }
+
 }
diff --git a/pom.xml b/pom.xml
index 021fa019..f4b314f0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -448,8 +448,8 @@
             </dependency>
             <dependency>
                 <groupId>org.mockito</groupId>
-                <artifactId>mockito-all</artifactId>
-                <version>1.9.5</version>
+                <artifactId>mockito-core</artifactId>
+                <version>3.2.4</version>
                 <scope>test</scope>
             </dependency>
             <dependency>


Test patch:
diff --git a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/DigitsRuleTest.java b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/DigitsRuleTest.java
new file mode 100644
index 00000000..dd911c38
--- /dev/null
+++ b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/DigitsRuleTest.java
@@ -0,0 +1,146 @@
+/**
+ * Copyright © 2010-2017 Nokia
+ *
+ * Licensed 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.jsonschema2pojo.rules;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.sun.codemodel.JAnnotationUse;
+import com.sun.codemodel.JFieldVar;
+import org.jsonschema2pojo.GenerationConfig;
+import org.jsonschema2pojo.NoopAnnotator;
+import org.jsonschema2pojo.SchemaStore;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.Digits;
+import javax.validation.constraints.Size;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Random;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests {@link DigitsRuleTest}
+ */
+@RunWith(Parameterized.class)
+public class DigitsRuleTest {
+
+    private final boolean isApplicable;
+    private DigitsRule rule;
+    private Class<?> fieldClass;
+    @Mock
+    private GenerationConfig config;
+    @Mock
+    private JsonNode node;
+    @Mock
+    private JsonNode subNodeInteger;
+    @Mock
+    private JsonNode subNodeFractional;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private JFieldVar fieldVar;
+    @Mock
+    private JAnnotationUse annotation;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return asList(new Object[][] {
+                { true, BigDecimal.class },
+                { true, BigInteger.class },
+                { true, String.class },
+                { true, Byte.class },
+                { true, Short.class },
+                { true, Integer.class },
+                { true, Long.class },
+                { false, Float.class },
+                { false, Double.class },
+        });
+    }
+
+
+    public DigitsRuleTest(boolean isApplicable, Class<?> fieldClass) {
+        this.isApplicable = isApplicable;
+        this.fieldClass = fieldClass;
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        rule = new DigitsRule(new RuleFactory(config, new NoopAnnotator(), new SchemaStore()));
+    }
+
+    @Test
+    public void testHasIntegerAndFractionalDigits() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int intValue = new Random().nextInt();
+        final int fractionalValue = new Random().nextInt();
+
+        when(subNodeInteger.asInt()).thenReturn(intValue);
+        when(subNodeFractional.asInt()).thenReturn(fractionalValue);
+        when(node.get("integerDigits")).thenReturn(subNodeInteger);
+        when(node.get("fractionalDigits")).thenReturn(subNodeFractional);
+        when(fieldVar.annotate(Digits.class)).thenReturn(annotation);
+        when(node.has("integerDigits")).thenReturn(true);
+        when(node.has("fractionalDigits")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Digits.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("integer", intValue);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("fraction", fractionalValue);
+    }
+
+    @Test
+    public void testNotUsed() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        when(node.has("integerDigits")).thenReturn(false);
+        when(node.has("fractionalDigits")).thenReturn(false);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(Size.class);
+        verify(annotation, never()).param(anyString(), anyInt());
+    }
+
+    @Test
+    public void jsrDisable() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(false);
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(DecimalMin.class);
+        verify(annotation, never()).param(anyString(), anyInt());
+    }
+
+}
\ No newline at end of file
diff --git a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRuleTest.java b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRuleTest.java
new file mode 100644
index 00000000..7ce232d0
--- /dev/null
+++ b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinItemsMaxItemsRuleTest.java
@@ -0,0 +1,203 @@
+/**
+ * Copyright © 2010-2017 Nokia
+ *
+ * Licensed 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.jsonschema2pojo.rules;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.sun.codemodel.JAnnotationUse;
+import com.sun.codemodel.JFieldVar;
+import org.jsonschema2pojo.GenerationConfig;
+import org.jsonschema2pojo.NoopAnnotator;
+import org.jsonschema2pojo.SchemaStore;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Random;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests {@link MinItemsMaxItemsRuleTest}
+ */
+@RunWith(Parameterized.class)
+public class MinItemsMaxItemsRuleTest {
+
+    private final boolean isApplicable;
+    private MinItemsMaxItemsRule rule;
+    private Class<?> fieldClass;
+    @Mock
+    private GenerationConfig config;
+    @Mock
+    private JsonNode node;
+    @Mock
+    private JsonNode subNode;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private JFieldVar fieldVar;
+    @Mock
+    private JAnnotationUse annotation;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return asList(new Object[][] {
+                { true, String.class },
+                { true, Collection.class },
+                { true, Map.class },
+                { true, Array.class },
+                { false, Byte.class },
+                { false, Short.class },
+                { false, Integer.class },
+                { false, Long.class },
+                { false, Float.class },
+                { false, Double.class },
+        });
+    }
+
+
+    public MinItemsMaxItemsRuleTest(boolean isApplicable, Class<?> fieldClass) {
+        this.isApplicable = isApplicable;
+        this.fieldClass = fieldClass;
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        rule = new MinItemsMaxItemsRule(new RuleFactory(config, new NoopAnnotator(), new SchemaStore()));
+    }
+
+    @Test
+    public void testMinLength() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int minValue = new Random().nextInt();
+        when(subNode.asInt()).thenReturn(minValue);
+        when(node.get("minItems")).thenReturn(subNode);
+        when(fieldVar.annotate(Size.class)).thenReturn(annotation);
+        when(node.has("minItems")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Size.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("min", minValue);
+        verify(annotation, never()).param(eq("max"), anyString());
+    }
+
+    @Test
+    public void testMaxLength() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int maxValue = new Random().nextInt();
+        when(subNode.asInt()).thenReturn(maxValue);
+        when(node.get("maxItems")).thenReturn(subNode);
+        when(fieldVar.annotate(Size.class)).thenReturn(annotation);
+        when(node.has("maxItems")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Size.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("max", maxValue);
+        verify(annotation, never()).param(eq("min"), anyInt());
+    }
+
+    @Test
+    public void testMaxAndMinLength() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int minValue = new Random().nextInt();
+        final int maxValue = new Random().nextInt();
+        JsonNode maxSubNode = Mockito.mock(JsonNode.class);
+        when(subNode.asInt()).thenReturn(minValue);
+        when(maxSubNode.asInt()).thenReturn(maxValue);
+        when(node.get("minItems")).thenReturn(subNode);
+        when(node.get("maxItems")).thenReturn(maxSubNode);
+        when(fieldVar.annotate(Size.class)).thenReturn(annotation);
+        when(node.has("minItems")).thenReturn(true);
+        when(node.has("maxItems")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Size.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("min", minValue);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("max", maxValue);
+    }
+
+    @Test
+    public void testMaxAndMinLengthGenericsOnType() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int minValue = new Random().nextInt();
+        final int maxValue = new Random().nextInt();
+        JsonNode maxSubNode = Mockito.mock(JsonNode.class);
+        when(subNode.asInt()).thenReturn(minValue);
+        when(maxSubNode.asInt()).thenReturn(maxValue);
+        when(node.get("minItems")).thenReturn(subNode);
+        when(node.get("maxItems")).thenReturn(maxSubNode);
+        when(fieldVar.annotate(Size.class)).thenReturn(annotation);
+        when(node.has("minItems")).thenReturn(true);
+        when(node.has("maxItems")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName() + "<String>");
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Size.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("min", minValue);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("max", maxValue);
+    }
+
+    @Test
+    public void testNotUsed() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        when(node.has("minItems")).thenReturn(false);
+        when(node.has("maxItems")).thenReturn(false);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(Size.class);
+        verify(annotation, never()).param(anyString(), anyInt());
+    }
+
+    @Test
+    public void jsrDisable() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(false);
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(Size.class);
+        verify(annotation, never()).param(anyString(), anyInt());
+    }
+}
\ No newline at end of file
diff --git a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRuleTest.java b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRuleTest.java
new file mode 100644
index 00000000..1ae9a2be
--- /dev/null
+++ b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinLengthMaxLengthRuleTest.java
@@ -0,0 +1,203 @@
+/**
+ * Copyright © 2010-2017 Nokia
+ *
+ * Licensed 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.jsonschema2pojo.rules;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.sun.codemodel.JAnnotationUse;
+import com.sun.codemodel.JFieldVar;
+import org.jsonschema2pojo.GenerationConfig;
+import org.jsonschema2pojo.NoopAnnotator;
+import org.jsonschema2pojo.SchemaStore;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Random;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests {@link MinLengthMaxLengthRuleTest}
+ */
+@RunWith(Parameterized.class)
+public class MinLengthMaxLengthRuleTest {
+
+    private final boolean isApplicable;
+    private MinLengthMaxLengthRule rule;
+    private Class<?> fieldClass;
+    @Mock
+    private GenerationConfig config;
+    @Mock
+    private JsonNode node;
+    @Mock
+    private JsonNode subNode;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private JFieldVar fieldVar;
+    @Mock
+    private JAnnotationUse annotation;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return asList(new Object[][] {
+                { true, String.class },
+                { true, Collection.class },
+                { true, Map.class },
+                { true, Array.class },
+                { false, Byte.class },
+                { false, Short.class },
+                { false, Integer.class },
+                { false, Long.class },
+                { false, Float.class },
+                { false, Double.class },
+        });
+    }
+
+
+    public MinLengthMaxLengthRuleTest(boolean isApplicable, Class<?> fieldClass) {
+        this.isApplicable = isApplicable;
+        this.fieldClass = fieldClass;
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        rule = new MinLengthMaxLengthRule(new RuleFactory(config, new NoopAnnotator(), new SchemaStore()));
+    }
+
+    @Test
+    public void testMinLength() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int minValue = new Random().nextInt();
+        when(subNode.asInt()).thenReturn(minValue);
+        when(node.get("minLength")).thenReturn(subNode);
+        when(fieldVar.annotate(Size.class)).thenReturn(annotation);
+        when(node.has("minLength")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Size.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("min", minValue);
+        verify(annotation, never()).param(eq("max"), anyString());
+    }
+
+    @Test
+    public void testMaxLength() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int maxValue = new Random().nextInt();
+        when(subNode.asInt()).thenReturn(maxValue);
+        when(node.get("maxLength")).thenReturn(subNode);
+        when(fieldVar.annotate(Size.class)).thenReturn(annotation);
+        when(node.has("maxLength")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Size.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("max", maxValue);
+        verify(annotation, never()).param(eq("min"), anyInt());
+    }
+
+    @Test
+    public void testMaxAndMinLength() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int minValue = new Random().nextInt();
+        final int maxValue = new Random().nextInt();
+        JsonNode maxSubNode = Mockito.mock(JsonNode.class);
+        when(subNode.asInt()).thenReturn(minValue);
+        when(maxSubNode.asInt()).thenReturn(maxValue);
+        when(node.get("minLength")).thenReturn(subNode);
+        when(node.get("maxLength")).thenReturn(maxSubNode);
+        when(fieldVar.annotate(Size.class)).thenReturn(annotation);
+        when(node.has("minLength")).thenReturn(true);
+        when(node.has("maxLength")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Size.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("min", minValue);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("max", maxValue);
+    }
+
+    @Test
+    public void testMaxAndMinLengthGenericsOnType() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final int minValue = new Random().nextInt();
+        final int maxValue = new Random().nextInt();
+        JsonNode maxSubNode = Mockito.mock(JsonNode.class);
+        when(subNode.asInt()).thenReturn(minValue);
+        when(maxSubNode.asInt()).thenReturn(maxValue);
+        when(node.get("minLength")).thenReturn(subNode);
+        when(node.get("maxLength")).thenReturn(maxSubNode);
+        when(fieldVar.annotate(Size.class)).thenReturn(annotation);
+        when(node.has("minLength")).thenReturn(true);
+        when(node.has("maxLength")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName() + "<String>");
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Size.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("min", minValue);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("max", maxValue);
+    }
+
+    @Test
+    public void testNotUsed() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        when(node.has("minLength")).thenReturn(false);
+        when(node.has("maxLength")).thenReturn(false);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(Size.class);
+        verify(annotation, never()).param(anyString(), anyInt());
+    }
+
+    @Test
+    public void jsrDisable() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(false);
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(Size.class);
+        verify(annotation, never()).param(anyString(), anyInt());
+    }
+}
\ No newline at end of file
diff --git a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinimumMaximumRuleTest.java b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinimumMaximumRuleTest.java
new file mode 100644
index 00000000..2f8345f0
--- /dev/null
+++ b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/MinimumMaximumRuleTest.java
@@ -0,0 +1,189 @@
+/**
+ * Copyright © 2010-2017 Nokia
+ *
+ * Licensed 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.jsonschema2pojo.rules;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.sun.codemodel.JAnnotationUse;
+import com.sun.codemodel.JFieldVar;
+import org.jsonschema2pojo.GenerationConfig;
+import org.jsonschema2pojo.NoopAnnotator;
+import org.jsonschema2pojo.SchemaStore;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import javax.validation.constraints.DecimalMax;
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.Size;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Random;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests {@link MinimumMaximumRuleTest}
+ */
+@RunWith(Parameterized.class)
+public class MinimumMaximumRuleTest {
+
+    private final boolean isApplicable;
+    private MinimumMaximumRule rule;
+    private Class<?> fieldClass;
+    @Mock
+    private GenerationConfig config;
+    @Mock
+    private JsonNode node;
+    @Mock
+    private JsonNode subNode;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private JFieldVar fieldVar;
+    @Mock
+    private JAnnotationUse annotationMax;
+    @Mock
+    private JAnnotationUse annotationMin;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return asList(new Object[][] {
+                { true, BigDecimal.class },
+                { true, BigInteger.class },
+                { true, String.class },
+                { true, Byte.class },
+                { true, Short.class },
+                { true, Integer.class },
+                { true, Long.class },
+                { false, Float.class },
+                { false, Double.class },
+        });
+    }
+
+
+    public MinimumMaximumRuleTest(boolean isApplicable, Class<?> fieldClass) {
+        this.isApplicable = isApplicable;
+        this.fieldClass = fieldClass;
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        rule = new MinimumMaximumRule(new RuleFactory(config, new NoopAnnotator(), new SchemaStore()));
+    }
+
+    @Test
+    public void testMinimum() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final String minValue = Integer.toString(new Random().nextInt());
+        when(subNode.asText()).thenReturn(minValue);
+        when(node.get("minimum")).thenReturn(subNode);
+        when(fieldVar.annotate(DecimalMin.class)).thenReturn(annotationMin);
+        when(node.has("minimum")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(DecimalMin.class);
+        verify(annotationMin, times(isApplicable ? 1 : 0)).param("value", minValue);
+        verify(fieldVar, never()).annotate(DecimalMax.class);
+        verify(annotationMax, never()).param(eq("value"), anyString());
+    }
+
+    @Test
+    public void testMaximum() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final String maxValue = Integer.toString(new Random().nextInt());
+        when(subNode.asText()).thenReturn(maxValue);
+        when(node.get("maximum")).thenReturn(subNode);
+        when(fieldVar.annotate(DecimalMax.class)).thenReturn(annotationMax);
+        when(node.has("maximum")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(DecimalMax.class);
+        verify(annotationMax, times(isApplicable ? 1 : 0)).param("value", maxValue);
+        verify(fieldVar, never()).annotate(DecimalMin.class);
+        verify(annotationMin, never()).param(eq("value"), anyString());
+    }
+
+    @Test
+    public void testMaximumAndMinimum() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final String minValue = Integer.toString(new Random().nextInt());
+        final String maxValue = Integer.toString(new Random().nextInt());
+        JsonNode maxSubNode = Mockito.mock(JsonNode.class);
+        when(subNode.asText()).thenReturn(minValue);
+        when(maxSubNode.asText()).thenReturn(maxValue);
+        when(node.get("minimum")).thenReturn(subNode);
+        when(node.get("maximum")).thenReturn(maxSubNode);
+        when(fieldVar.annotate(DecimalMin.class)).thenReturn(annotationMin);
+        when(fieldVar.annotate(DecimalMax.class)).thenReturn(annotationMax);
+        when(node.has("minimum")).thenReturn(true);
+        when(node.has("maximum")).thenReturn(true);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(DecimalMin.class);
+        verify(annotationMin, times(isApplicable ? 1 : 0)).param("value", minValue);
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(DecimalMax.class);
+        verify(annotationMax, times(isApplicable ? 1 : 0)).param("value", maxValue);
+    }
+
+    @Test
+    public void testNotUsed() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        when(node.has("minimum")).thenReturn(false);
+        when(node.has("maximum")).thenReturn(false);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(Size.class);
+        verify(annotationMin, never()).param(anyString(), anyString());
+        verify(annotationMax, never()).param(anyString(), anyString());
+    }
+
+    @Test
+    public void jsrDisable() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(false);
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(DecimalMin.class);
+        verify(annotationMin, never()).param(anyString(), anyString());
+        verify(fieldVar, never()).annotate(DecimalMax.class);
+        verify(annotationMax, never()).param(anyString(), anyString());
+    }
+}
\ No newline at end of file
diff --git a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/PatternRuleTest.java b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/PatternRuleTest.java
new file mode 100644
index 00000000..877d5c35
--- /dev/null
+++ b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/PatternRuleTest.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright © 2010-2017 Nokia
+ *
+ * Licensed 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.jsonschema2pojo.rules;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.sun.codemodel.JAnnotationUse;
+import com.sun.codemodel.JFieldVar;
+import org.jsonschema2pojo.GenerationConfig;
+import org.jsonschema2pojo.NoopAnnotator;
+import org.jsonschema2pojo.SchemaStore;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import javax.validation.constraints.Pattern;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests {@link PatternRuleTest}
+ */
+@RunWith(Parameterized.class)
+public class PatternRuleTest {
+
+    private final boolean isApplicable;
+    private PatternRule rule;
+    private Class<?> fieldClass;
+    @Mock
+    private GenerationConfig config;
+    @Mock
+    private JsonNode node;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private JFieldVar fieldVar;
+    @Mock
+    private JAnnotationUse annotation;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return asList(new Object[][] {
+                { true, String.class },
+                { false, UUID.class },
+                { false, Collection.class },
+                { false, Map.class },
+                { false, Array.class },
+                { false, Byte.class },
+                { false, Short.class },
+                { false, Integer.class },
+                { false, Long.class },
+                { false, Float.class },
+                { false, Double.class },
+        });
+    }
+
+
+    public PatternRuleTest(boolean isApplicable, Class<?> fieldClass) {
+        this.isApplicable = isApplicable;
+        this.fieldClass = fieldClass;
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        rule = new PatternRule(new RuleFactory(config, new NoopAnnotator(), new SchemaStore()));
+    }
+
+    @Test
+    public void testRegex() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(true);
+        final String patternValue = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$";
+
+        when(node.asText()).thenReturn(patternValue);
+        when(fieldVar.annotate(Pattern.class)).thenReturn(annotation);
+        when(fieldVar.type().boxify().fullName()).thenReturn(fieldClass.getTypeName());
+
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, times(isApplicable ? 1 : 0)).annotate(Pattern.class);
+        verify(annotation, times(isApplicable ? 1 : 0)).param("regexp", patternValue);
+    }
+
+    @Test
+    public void jsrDisable() {
+        when(config.isIncludeJsr303Annotations()).thenReturn(false);
+        JFieldVar result = rule.apply("node", node, null, fieldVar, null);
+        assertSame(fieldVar, result);
+
+        verify(fieldVar, never()).annotate(Pattern.class);
+        verify(annotation, never()).param(anyString(), anyString());
+    }
+
+}
\ No newline at end of file
diff --git a/jsonschema2pojo-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/jsonschema2pojo-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 00000000..ca6ee9ce
--- /dev/null
+++ b/jsonschema2pojo-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1,1 @@
+mock-maker-inline
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/pom.xml b/jsonschema2pojo-integration-tests/pom.xml
index 5275f830..245a5083 100644
--- a/jsonschema2pojo-integration-tests/pom.xml
+++ b/jsonschema2pojo-integration-tests/pom.xml
@@ -149,7 +149,7 @@
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
+            <artifactId>mockito-core</artifactId>
         </dependency>
         <dependency>
            <groupId>org.robolectric</groupId>
diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java
index 1944130e..3cdbad89 100644
--- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java
+++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java
@@ -30,6 +30,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
 import javax.validation.ConstraintViolation;
 import javax.validation.Validation;
@@ -71,11 +72,12 @@ public class IncludeJsr303AnnotationsIT {
 
         Class generatedType = resultsClassLoader.loadClass("com.example.Minimum");
 
-        Object validInstance = createInstanceWithPropertyValue(generatedType, "minimum", 2.0d);
+        Object validInstance = createInstanceWithPropertyValue(generatedType, "minimum", 2);
+        setInstancePropertyValue(validInstance, "minimumNotConstrained", 1.5);
 
         assertNumberOfConstraintViolationsOn(validInstance, is(0));
 
-        Object invalidInstance = createInstanceWithPropertyValue(generatedType, "minimum", 0.9d);
+        Object invalidInstance = createInstanceWithPropertyValue(generatedType, "minimum", 0);
 
         assertNumberOfConstraintViolationsOn(invalidInstance, is(1));
 
@@ -89,11 +91,12 @@ public class IncludeJsr303AnnotationsIT {
 
         Class generatedType = resultsClassLoader.loadClass("com.example.Maximum");
 
-        Object validInstance = createInstanceWithPropertyValue(generatedType, "maximum", 8.9d);
+        Object validInstance = createInstanceWithPropertyValue(generatedType, "maximum", 8);
+        setInstancePropertyValue(validInstance, "maximumNotConstrained", 10.6);
 
         assertNumberOfConstraintViolationsOn(validInstance, is(0));
 
-        Object invalidInstance = createInstanceWithPropertyValue(generatedType, "maximum", 10.9d);
+        Object invalidInstance = createInstanceWithPropertyValue(generatedType, "maximum", 10);
 
         assertNumberOfConstraintViolationsOn(invalidInstance, is(1));
 
@@ -108,6 +111,7 @@ public class IncludeJsr303AnnotationsIT {
         Class generatedType = resultsClassLoader.loadClass("com.example.MinItems");
 
         Object validInstance = createInstanceWithPropertyValue(generatedType, "minItems", asList(1, 2, 3, 4, 5, 6));
+        setInstancePropertyValue(validInstance, "minItemsNotApplicable", UUID.randomUUID());
 
         assertNumberOfConstraintViolationsOn(validInstance, is(0));
 
@@ -126,6 +130,7 @@ public class IncludeJsr303AnnotationsIT {
         Class generatedType = resultsClassLoader.loadClass("com.example.MaxItems");
 
         Object validInstance = createInstanceWithPropertyValue(generatedType, "maxItems", asList(1, 2, 3));
+        setInstancePropertyValue(validInstance, "maxItemsNotApplicable", UUID.randomUUID());
 
         assertNumberOfConstraintViolationsOn(validInstance, is(0));
 
@@ -165,6 +170,7 @@ public class IncludeJsr303AnnotationsIT {
         Class generatedType = resultsClassLoader.loadClass("com.example.Pattern");
 
         Object validInstance = createInstanceWithPropertyValue(generatedType, "pattern", "abc123");
+        setInstancePropertyValue(validInstance, "patternNotApplicable", UUID.randomUUID());
 
         assertNumberOfConstraintViolationsOn(validInstance, is(0));
 
@@ -199,6 +205,7 @@ public class IncludeJsr303AnnotationsIT {
         Class generatedType = resultsClassLoader.loadClass("com.example.MinLength");
 
         Object validInstance = createInstanceWithPropertyValue(generatedType, "minLength", "Long enough");
+        setInstancePropertyValue(validInstance, "minLengthNotApplicable", UUID.randomUUID());
 
         assertNumberOfConstraintViolationsOn(validInstance, is(0));
 
@@ -216,6 +223,7 @@ public class IncludeJsr303AnnotationsIT {
         Class generatedType = resultsClassLoader.loadClass("com.example.MaxLength");
 
         Object validInstance = createInstanceWithPropertyValue(generatedType, "maxLength", "Short");
+        setInstancePropertyValue(validInstance, "maxLengthNotApplicable", UUID.randomUUID());
 
         assertNumberOfConstraintViolationsOn(validInstance, is(0));
 
@@ -234,6 +242,7 @@ public class IncludeJsr303AnnotationsIT {
 
         // positive value
         Object validInstance = createInstanceWithPropertyValue(generatedType, "decimal", new BigDecimal("12345.1234567890"));
+        setInstancePropertyValue(validInstance, "digitsNotApplicable", Collections.singletonList("12345.12345678901"));
 
         assertNumberOfConstraintViolationsOn(validInstance, is(0));
 
@@ -331,15 +340,15 @@ public class IncludeJsr303AnnotationsIT {
 
     @SuppressWarnings("unchecked")
     @Test
-    public void jsr303AnnotionsValidatedForAdditionalProperties() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+    public void jar303AnnotationsValidatedForAdditionalProperties() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
         ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/jsr303/validAdditionalProperties.json", "com.example", config("includeJsr303Annotations", true));
 
         Class parentType = resultsClassLoader.loadClass("com.example.ValidAdditionalProperties");
         Object parent = parentType.newInstance();
 
         Class subPropertyType = resultsClassLoader.loadClass("com.example.ValidAdditionalPropertiesProperty");
-        Object validSubPropertyInstance = createInstanceWithPropertyValue(subPropertyType, "maximum", 9.0D);
-        Object invalidSubPropertyInstance = createInstanceWithPropertyValue(subPropertyType, "maximum", 11.0D);
+        Object validSubPropertyInstance = createInstanceWithPropertyValue(subPropertyType, "maximum", 9);
+        Object invalidSubPropertyInstance = createInstanceWithPropertyValue(subPropertyType, "maximum", 11);
 
         Method setter = parentType.getMethod("setAdditionalProperty", String.class, subPropertyType);
 
@@ -367,6 +376,16 @@ public class IncludeJsr303AnnotationsIT {
         }
     }
 
+    private static void setInstancePropertyValue(Object instance, String propertyName, Object propertyValue) {
+        try {
+            PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyName, instance.getClass());
+            propertyDescriptor.getWriteMethod().invoke(instance, propertyValue);
+
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private static Matcher<File> containsText(String searchText) {
         return new FileSearchMatcher(searchText);
     }
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/digits.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/digits.json
index 22791f28..f6a0ba77 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/digits.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/digits.json
@@ -5,6 +5,11 @@
       "type": "number",
       "integerDigits": 5,
       "fractionalDigits": 10
+    },
+    "digitsNotApplicable": {
+      "type" : "array",
+      "integerDigits": 5,
+      "fractionalDigits": 10
     }
   }
 }
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maxItems.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maxItems.json
index eee3cfbb..5922e0d8 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maxItems.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maxItems.json
@@ -4,6 +4,11 @@
         "maxItems" : {
             "type" : "array",
             "maxItems" : 5            
+        },
+        "maxItemsNotApplicable" : {
+            "type" : "string",
+            "format": "uuid",
+            "maxItems" : 5
         }
     }
 }
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maxLength.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maxLength.json
index 4fd72ef4..89169bfe 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maxLength.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maxLength.json
@@ -4,6 +4,11 @@
         "maxLength" : {
             "type" : "string",
             "maxLength" : 10
+        },
+        "maxLengthNotApplicable" : {
+            "type" : "string",
+            "format": "uuid",
+            "maxLength" : 5
         }
     }
 }
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maximum.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maximum.json
index 462122d0..a49c3e08 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maximum.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/maximum.json
@@ -2,8 +2,12 @@
     "type" : "object",
     "properties" : {
         "maximum" : {
+            "type" : "integer",
+            "maximum" : 9
+        },
+        "maximumNotConstrained" : {
             "type" : "number",
-            "maximum" : 9.9
+            "maximum" : 9.5
         }
     }
 }
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minItems.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minItems.json
index a2f79696..c85fb427 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minItems.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minItems.json
@@ -4,6 +4,11 @@
         "minItems" : {
             "type" : "array",
             "minItems" : 5
+        },
+        "minItemsNotApplicable" : {
+            "type" : "string",
+            "format": "uuid",
+            "minItems" : 5
         }
     }
 }
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minLength.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minLength.json
index 0cbb38b8..9e447f0f 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minLength.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minLength.json
@@ -4,6 +4,11 @@
         "minLength" : {
             "type" : "string",
             "minLength" : 10
+        },
+        "minLengthNotApplicable" : {
+            "type" : "string",
+            "format": "uuid",
+            "minLength" : 64
         }
     }
 }
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minimum.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minimum.json
index 14bc458c..0eef147a 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minimum.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/minimum.json
@@ -2,8 +2,12 @@
     "type" : "object",
     "properties" : {
         "minimum" : {
+            "type" : "integer",
+            "minimum" : 1
+        },
+        "minimumNotConstrained" : {
             "type" : "number",
-            "minimum" : 1.1
+            "minimum" : 2.1
         }
     }
 }
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/pattern.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/pattern.json
index 3db53ed1..da869625 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/pattern.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/pattern.json
@@ -4,6 +4,11 @@
         "pattern" : {
             "type" : "string",
             "pattern" : "abc.*"
+        },
+        "patternNotApplicable" : {
+            "type" : "string",
+            "format": "uuid",
+            "pattern" : "xzy.*"
         }
     }
 }
\ No newline at end of file
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/validAdditionalProperties.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/validAdditionalProperties.json
index f61b7650..a85fd97b 100644
--- a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/validAdditionalProperties.json
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/validAdditionalProperties.json
@@ -3,8 +3,8 @@
     "additionalProperties" : {
         "properties" : {
             "maximum" : {
-                "type" : "number",
-                "maximum" : 9.9
+                "type" : "integer",
+                "maximum" : 9
             }
         },
         "additionalProperties" : false