Skip to content

Commit 10a8751

Browse files
javier-godoypaodb
authored andcommitted
feat: initial implementation
1 parent 656a59c commit 10a8751

21 files changed

+788
-264
lines changed

README.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
[![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/template-add-on)
2-
[![Stars on vaadin.com/directory](https://img.shields.io/vaadin-directory/star/template-add-on.svg)](https://vaadin.com/directory/component/template-add-on)
3-
[![Build Status](https://jenkins.flowingcode.com/job/template-addon/badge/icon)](https://jenkins.flowingcode.com/job/template-addon)
4-
[![Maven Central](https://img.shields.io/maven-central/v/com.flowingcode.vaadin.addons/template-addon)](https://mvnrepository.com/artifact/com.flowingcode.vaadin.addons/template-addon)
5-
[![Javadoc](https://img.shields.io/badge/javadoc-00b4f0)](https://javadoc.flowingcode.com/artifact/com.flowingcode.vaadin.addons/template-addon)
1+
[![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/regular-expression-field-add-on)
2+
[![Stars on vaadin.com/directory](https://img.shields.io/vaadin-directory/star/regular-expression-field-add-on.svg)](https://vaadin.com/directory/component/regular-expression-field-add-on)
3+
[![Build Status](https://jenkins.flowingcode.com/job/regular-expression-field-addon/badge/icon)](https://jenkins.flowingcode.com/job/regular-expression-field-addon)
4+
[![Maven Central](https://img.shields.io/maven-central/v/com.flowingcode.vaadin.addons/regular-expression-field-addon)](https://mvnrepository.com/artifact/com.flowingcode.vaadin.addons/regular-expression-field-addon)
5+
[![Javadoc](https://img.shields.io/badge/javadoc-00b4f0)](https://javadoc.flowingcode.com/artifact/com.flowingcode.vaadin.addons/regular-expression-field-addon)
66

7-
# Template Add-on
7+
# Regular Expression Field Add-on
88

9-
This is a template project for building new Vaadin 24 add-ons
9+
A field for Vaadin 24 that assists in creating regular expressions.
1010

1111
## Features
1212

13-
* List the features of your add-on in here
13+
* A ComboBox option for creating simple regular expressions, such as "contains."
14+
* Additional options for patterns like "starts with" and "ends with."
15+
* An advanced mode that assists in creating complex regular expressions with automatic validation.
16+
1417

1518
## Online demo
1619

17-
[Online demo here](http://addonsv24.flowingcode.com/template)
20+
[Online demo here](http://addonsv24.flowingcode.com/regular-expression-field)
1821

1922
## Download release
2023

21-
[Available in Vaadin Directory](https://vaadin.com/directory/component/template-add-on)
24+
[Available in Vaadin Directory](https://vaadin.com/directory/component/regular-expression-field-add-on)
2225

2326
### Maven install
2427

@@ -27,7 +30,7 @@ Add the following dependencies in your pom.xml file:
2730
```xml
2831
<dependency>
2932
<groupId>com.flowingcode.vaadin.addons</groupId>
30-
<artifactId>template-addon</artifactId>
33+
<artifactId>regular-expression-field-addon</artifactId>
3134
<version>X.Y.Z</version>
3235
</dependency>
3336
```
@@ -69,7 +72,7 @@ Then, follow these steps for creating a contribution:
6972

7073
This add-on is distributed under Apache License 2.0. For license terms, see LICENSE.txt.
7174

72-
TEMPLATE_ADDON is written by Flowing Code S.A.
75+
Regular Expression Field Add-On is written by Flowing Code S.A.
7376

7477
# Developer Guide
7578

pom.xml

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
<modelVersion>4.0.0</modelVersion>
66

77
<groupId>com.flowingcode.vaadin.addons</groupId>
8-
<artifactId>template-addon</artifactId>
8+
<artifactId>regular-expression-field-addon</artifactId>
99
<version>1.0.0-SNAPSHOT</version>
10-
<name>Template Add-on</name>
11-
<description>Template Add-on for Vaadin Flow</description>
10+
<name>Regular Expression Field Add-on</name>
11+
<description>Regular Expression Field Add-on for Vaadin Flow</description>
1212
<url>https://www.flowingcode.com/en/open-source/</url>
1313

1414
<properties>
15-
<vaadin.version>24.4.6</vaadin.version>
15+
<vaadin.version>24.7.0</vaadin.version>
1616
<selenium.version>4.10.0</selenium.version>
1717
<maven.compiler.source>17</maven.compiler.source>
1818
<maven.compiler.target>17</maven.compiler.target>
@@ -21,7 +21,6 @@
2121
<drivers.dir>${project.basedir}/drivers</drivers.dir>
2222
<jetty.version>11.0.20</jetty.version>
2323
<flowingcode.commons.demo.version>4.2.0</flowingcode.commons.demo.version>
24-
<frontend.hotdeploy>true</frontend.hotdeploy>
2524
</properties>
2625

2726
<organization>
@@ -39,9 +38,9 @@
3938
</licenses>
4039

4140
<scm>
42-
<url>https://github.com/FlowingCode/AddonStarter24</url>
43-
<connection>scm:git:git://github.com/FlowingCode/AddonStarter24.git</connection>
44-
<developerConnection>scm:git:ssh://[email protected]:/FlowingCode/AddonStarter24.git</developerConnection>
41+
<url>https://github.com/FlowingCode/RegularExpressionField</url>
42+
<connection>scm:git:git://github.com/FlowingCode/RegularExpressionField.git</connection>
43+
<developerConnection>scm:git:ssh://[email protected]:/FlowingCode/RegularExpressionField.git</developerConnection>
4544
<tag>master</tag>
4645
</scm>
4746

@@ -122,6 +121,12 @@
122121
<artifactId>vaadin-core</artifactId>
123122
<optional>true</optional>
124123
</dependency>
124+
<dependency>
125+
<groupId>org.projectlombok</groupId>
126+
<artifactId>lombok</artifactId>
127+
<version>1.18.36</version>
128+
<scope>provided</scope>
129+
</dependency>
125130
<dependency>
126131
<groupId>com.flowingcode.vaadin.addons.demo</groupId>
127132
<artifactId>commons-demo</artifactId>
@@ -264,15 +269,12 @@
264269
<scan>3</scan>
265270
<!-- Use test scope because the UI/demo classes are in the test package. -->
266271
<useTestScope>true</useTestScope>
267-
<webApp>
268-
<resourceBases>
269-
<resourceBase>src/test/resources/META-INF/resources</resourceBase>
270-
<resourceBase>src/main/resources/META-INF/resources</resourceBase>
271-
</resourceBases>
272-
</webApp>
273272
<supportedPackagings>
274273
<supportedPackaging>jar</supportedPackaging>
275274
</supportedPackagings>
275+
<systemProperties>
276+
<vaadin.frontend.hotdeploy>true</vaadin.frontend.hotdeploy>
277+
</systemProperties>
276278
</configuration>
277279
</plugin>
278280
</plugins>
@@ -503,8 +505,11 @@
503505
</build>
504506
</profile>
505507

506-
<profile>
508+
<profile>
507509
<id>demo-war</id>
510+
<properties>
511+
<vaadin.productionMode>true</vaadin.productionMode>
512+
</properties>
508513
<dependencies>
509514
<dependency>
510515
<groupId>com.vaadin</groupId>
@@ -540,15 +545,21 @@
540545
<goal>run</goal>
541546
</goals>
542547
<configuration>
543-
<target name="copy-demo">
544-
<copy todir="${project.basedir}/src/main">
548+
<tasks>
549+
<copy todir="${project.basedir}/src/main" failonerror="false">
545550
<fileset dir="${project.basedir}/src/test">
546551
<include name="**"/>
547552
<exclude name="**/it/*"/>
548553
<exclude name="**/test/*"/>
549554
</fileset>
550555
</copy>
551-
</target>
556+
<copy todir="${project.basedir}/src/main/resources/META-INF/resources/frontend" failonerror="false">
557+
<fileset dir="${project.basedir}/src/main/resources/META-INF/frontend"/>
558+
</copy>
559+
<delete failonerror="false">
560+
<fileset dir="${project.basedir}/src/main/resources/META-INF/frontend"/>
561+
</delete>
562+
</tasks>
552563
</configuration>
553564
</execution>
554565
</executions>
@@ -586,7 +597,6 @@
586597
</plugins>
587598
</build>
588599
</profile>
589-
590600
</profiles>
591601

592602
</project>
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*-
2+
* #%L
3+
* Regular Expression Field Add-on
4+
* %%
5+
* Copyright (C) 2025 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.vaadin.addons.regex;
21+
22+
import static com.flowingcode.vaadin.addons.regex.RegularExpressionOperator.ADVANCED;
23+
import static com.flowingcode.vaadin.addons.regex.RegularExpressionOperator.CONTAINS;
24+
import static com.flowingcode.vaadin.addons.regex.RegularExpressionOperator.ENDS_WITH;
25+
import static com.flowingcode.vaadin.addons.regex.RegularExpressionOperator.STARTS_WITH;
26+
import java.util.regex.Pattern;
27+
import java.util.regex.PatternSyntaxException;
28+
import lombok.AccessLevel;
29+
import lombok.AllArgsConstructor;
30+
import lombok.EqualsAndHashCode;
31+
import lombok.Getter;
32+
import lombok.NonNull;
33+
34+
/**
35+
* Represents a regular expression.
36+
*
37+
* <p>
38+
* This class assists in the creation and handling of regular expressions by providing pre-defined
39+
* operators for common patterns such as "starts with", "ends with", and "contains". It also
40+
* supports an advanced mode where users can input custom regular expressions.
41+
* </p>
42+
*
43+
* <p>
44+
* Instances of this class are immutable and validated upon creation.
45+
* </p>
46+
*
47+
* @author Javier Godoy
48+
*/
49+
@Getter
50+
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
51+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
52+
public final class RegularExpression {
53+
54+
private static final String ANY = ".*";
55+
56+
/** The operator defining the type of regular expression. */
57+
@EqualsAndHashCode.Include
58+
private final RegularExpressionOperator operator;
59+
60+
/** The input string used for generating the pattern. */
61+
@EqualsAndHashCode.Include
62+
private final String input;
63+
64+
/** The compiled {@code Pattern} for the regular expression. */
65+
private final Pattern pattern;
66+
67+
/**
68+
* Creates a new {@code RegularExpression} based on the specified {@code operator} and
69+
* {@code input}.
70+
*
71+
* @param operator the type of regular expression (e.g.,
72+
* {@link RegularExpressionOperator#CONTAINS})
73+
* @param input the string to be used for pattern creation
74+
* @throws PatternSyntaxException if the resulting pattern is invalid.
75+
*/
76+
public RegularExpression(@NonNull RegularExpressionOperator operator, @NonNull String input)
77+
throws PatternSyntaxException {
78+
this.operator = operator;
79+
this.input = input;
80+
81+
String regex = switch (operator) {
82+
case ADVANCED -> input;
83+
case CONTAINS -> ANY + quote(input) + ANY;
84+
case ENDS_WITH -> ANY + quote(input);
85+
case STARTS_WITH -> quote(input) + ANY;
86+
};
87+
88+
pattern = Pattern.compile(regex);
89+
}
90+
91+
private final static String CHARS = ".?+*\\[({$^|\\\\";
92+
93+
private final static Pattern SIMPLE_PATTERN =
94+
Pattern.compile("(?:[^CHARS]|\\\\[CHARS])+".replace("CHARS", CHARS));
95+
96+
private final static Pattern ESCAPE_PATTERN =
97+
Pattern.compile("[CHARS]".replace("CHARS", CHARS));
98+
99+
private final static Pattern UNQUOTE_PATTERN =
100+
Pattern.compile("\\\\([CHARS])".replace("CHARS", CHARS));
101+
102+
private static String quote(String input) {
103+
String s1 = ESCAPE_PATTERN.matcher(input).replaceAll("\\\\$0");
104+
String s2 = "\\Q" + input.replace("\\E", "\\E\\\\E\\Q") + "\\E";
105+
return (s1.length() < s2.length()) ? s1 : s2;
106+
}
107+
108+
/**
109+
* Creates a {@code RegularExpression} instance from a given {@link Pattern}.
110+
*
111+
* <p>
112+
* This method attempts to determine if the pattern corresponds to a simple
113+
* {@link RegularExpressionOperator} like "starts with", "ends with", or "contains". If it does, a
114+
* corresponding {@code RegularExpression} is returned. Otherwise, an advanced mode expression is
115+
* created.
116+
* </p>
117+
*
118+
* @param pattern the pattern to analyze
119+
*
120+
* @throws NullPointerException if {@code pattern} is {@code null}.
121+
*
122+
* @return a {@code RegularExpression} instance
123+
*/
124+
public static RegularExpression of(Pattern pattern) {
125+
126+
if (pattern == null) {
127+
throw new NullPointerException("Pattern cannot be null");
128+
}
129+
130+
String regex = pattern.pattern();
131+
boolean hasLeadingWildcard = false;
132+
boolean hasTrailingWildcard = false;
133+
if (regex.startsWith(ANY)) {
134+
hasLeadingWildcard = true;
135+
regex = regex.substring(2);
136+
}
137+
138+
if (regex.endsWith(ANY)) {
139+
hasTrailingWildcard = true;
140+
regex = regex.substring(0, regex.length() - 2);
141+
}
142+
143+
String input = null;
144+
if (hasLeadingWildcard || hasTrailingWildcard) {
145+
if (SIMPLE_PATTERN.matcher(regex).matches()) {
146+
input = UNQUOTE_PATTERN.matcher(regex).replaceAll("$1");
147+
} else if (regex.startsWith("\\Q") && regex.endsWith("\\E")) {
148+
input = regex.substring(2, regex.length() - 2).replace("\\E\\\\E\\Q", "\\E");
149+
}
150+
}
151+
152+
if (input != null) {
153+
if (hasLeadingWildcard && hasTrailingWildcard) {
154+
return new RegularExpression(CONTAINS, input);
155+
} else if (hasLeadingWildcard) {
156+
return new RegularExpression(ENDS_WITH, input);
157+
} else if (hasTrailingWildcard) {
158+
return new RegularExpression(STARTS_WITH, input);
159+
}
160+
}
161+
162+
return new RegularExpression(ADVANCED, pattern.pattern(), pattern);
163+
}
164+
165+
@Override
166+
public String toString() {
167+
return operator + " " + input;
168+
}
169+
170+
}

0 commit comments

Comments
 (0)