mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-22 23:35:50 +00:00
edited version (AhmedMagdyy)
This commit is contained in:
parent
6148ddd967
commit
d421931d19
22 changed files with 1500 additions and 92 deletions
70
pom.xml
70
pom.xml
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--suppress ALL -->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -32,11 +33,13 @@
|
||||||
|
|
||||||
<checkstyle.version>10.20.1</checkstyle.version>
|
<checkstyle.version>10.20.1</checkstyle.version>
|
||||||
<jacoco.version>0.8.12</jacoco.version>
|
<jacoco.version>0.8.12</jacoco.version>
|
||||||
<libsass.version>0.2.29</libsass.version>
|
<libsass.version>0.7.0</libsass.version>
|
||||||
<lifecycle-mapping>1.0.0</lifecycle-mapping>
|
<lifecycle-mapping>1.0.0</lifecycle-mapping>
|
||||||
<maven-checkstyle.version>3.6.0</maven-checkstyle.version>
|
<maven-checkstyle.version>3.6.0</maven-checkstyle.version>
|
||||||
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
|
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
|
||||||
<spring-format.version>0.0.43</spring-format.version>
|
<spring-format.version>0.0.43</spring-format.version>
|
||||||
|
<maven.compiler.source>23</maven.compiler.source>
|
||||||
|
<maven.compiler.target>23</maven.compiler.target>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -146,11 +149,39 @@
|
||||||
<artifactId>mysql</artifactId>
|
<artifactId>mysql</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.antlr</groupId>
|
||||||
|
<artifactId>antlr4-runtime</artifactId>
|
||||||
|
<version>4.13.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jakarta.xml.bind</groupId>
|
<groupId>jakarta.xml.bind</groupId>
|
||||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- AssertJ (for fluent assertions) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>3.24.2</version> <!-- Ensure the version exists in the Maven repository -->
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JUnit 5 (Jupiter) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.11.3</version> <!-- Check for the latest version -->
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@ -196,10 +227,27 @@
|
||||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
<version>${maven-checkstyle.version}</version>
|
<version>${maven-checkstyle.version}</version>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.antlr</groupId>
|
||||||
|
<artifactId>antlr4-runtime</artifactId>
|
||||||
|
<version>4.13.2</version> <!-- Use the latest version -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>5.11.3</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- AssertJ (for fluent assertions) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>3.24.2</version> <!-- Ensure the version exists in the Maven repository -->
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.puppycrawl.tools</groupId>
|
<groupId>com.puppycrawl.tools</groupId>
|
||||||
<artifactId>checkstyle</artifactId>
|
<artifactId>checkstyle</artifactId>
|
||||||
<version>${checkstyle.version}</version>
|
<version>10.12.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.spring.nohttp</groupId>
|
<groupId>io.spring.nohttp</groupId>
|
||||||
|
@ -209,7 +257,7 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>nohttp-checkstyle-validation</id>
|
<id>validation-checkstyle</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>check</goal>
|
<goal>check</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
@ -297,6 +345,18 @@
|
||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>libsass-maven-plugin-repo</id>
|
||||||
|
<url>https://plugins.gitlab.com/maven</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>jcenter</id>
|
||||||
|
<url>https://jcenter.bintray.com</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spring-plugins-release</id>
|
||||||
|
<url>https://repo.spring.io/plugins-release</url>
|
||||||
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
|
@ -341,6 +401,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<version>3.5.0</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>unpack</id>
|
<id>unpack</id>
|
||||||
|
@ -366,7 +427,8 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.gitlab.haynes</groupId>
|
<groupId>com.gitlab.haynes</groupId>
|
||||||
<artifactId>libsass-maven-plugin</artifactId>
|
<artifactId>libsass-maven-plugin</artifactId>
|
||||||
<version>${libsass.version}</version>
|
<version>0.7.0</version>
|
||||||
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<inputPath>${basedir}/src/main/scss/</inputPath>
|
<inputPath>${basedir}/src/main/scss/</inputPath>
|
||||||
<outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
|
<outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
|
||||||
|
|
233
src/antlr/JavaLexer.g4
Normal file
233
src/antlr/JavaLexer.g4
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
/*
|
||||||
|
[The "BSD licence"]
|
||||||
|
Copyright (c) 2013 Terence Parr, Sam Harwell
|
||||||
|
Copyright (c) 2017 Ivan Kochurkin (upgrade to Java 8)
|
||||||
|
Copyright (c) 2021 Michał Lorek (upgrade to Java 11)
|
||||||
|
Copyright (c) 2022 Michał Lorek (upgrade to Java 17)
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// $antlr-format alignTrailingComments true, columnLimit 150, maxEmptyLinesToKeep 1, reflowComments false, useTab false
|
||||||
|
// $antlr-format allowShortRulesOnASingleLine true, allowShortBlocksOnASingleLine true, minEmptyLines 0, alignSemicolons ownLine
|
||||||
|
// $antlr-format alignColons trailing, singleLineOverrulesHangingColon true, alignLexerCommands true, alignLabels true, alignTrailers true
|
||||||
|
|
||||||
|
lexer grammar JavaLexer;
|
||||||
|
|
||||||
|
// Keywords
|
||||||
|
|
||||||
|
ABSTRACT : 'abstract';
|
||||||
|
ASSERT : 'assert';
|
||||||
|
BOOLEAN : 'boolean';
|
||||||
|
BREAK : 'break';
|
||||||
|
BYTE : 'byte';
|
||||||
|
CASE : 'case';
|
||||||
|
CATCH : 'catch';
|
||||||
|
CHAR : 'char';
|
||||||
|
CLASS : 'class';
|
||||||
|
CONST : 'const';
|
||||||
|
CONTINUE : 'continue';
|
||||||
|
DEFAULT : 'default';
|
||||||
|
DO : 'do';
|
||||||
|
DOUBLE : 'double';
|
||||||
|
ELSE : 'else';
|
||||||
|
ENUM : 'enum';
|
||||||
|
EXTENDS : 'extends';
|
||||||
|
FINAL : 'final';
|
||||||
|
FINALLY : 'finally';
|
||||||
|
FLOAT : 'float';
|
||||||
|
FOR : 'for';
|
||||||
|
IF : 'if';
|
||||||
|
GOTO : 'goto';
|
||||||
|
IMPLEMENTS : 'implements';
|
||||||
|
IMPORT : 'import';
|
||||||
|
INSTANCEOF : 'instanceof';
|
||||||
|
INT : 'int';
|
||||||
|
INTERFACE : 'interface';
|
||||||
|
LONG : 'long';
|
||||||
|
NATIVE : 'native';
|
||||||
|
NEW : 'new';
|
||||||
|
PACKAGE : 'package';
|
||||||
|
PRIVATE : 'private';
|
||||||
|
PROTECTED : 'protected';
|
||||||
|
PUBLIC : 'public';
|
||||||
|
RETURN : 'return';
|
||||||
|
SHORT : 'short';
|
||||||
|
STATIC : 'static';
|
||||||
|
STRICTFP : 'strictfp';
|
||||||
|
SUPER : 'super';
|
||||||
|
SWITCH : 'switch';
|
||||||
|
SYNCHRONIZED : 'synchronized';
|
||||||
|
THIS : 'this';
|
||||||
|
THROW : 'throw';
|
||||||
|
THROWS : 'throws';
|
||||||
|
TRANSIENT : 'transient';
|
||||||
|
TRY : 'try';
|
||||||
|
VOID : 'void';
|
||||||
|
VOLATILE : 'volatile';
|
||||||
|
WHILE : 'while';
|
||||||
|
|
||||||
|
// Module related keywords
|
||||||
|
MODULE : 'module';
|
||||||
|
OPEN : 'open';
|
||||||
|
REQUIRES : 'requires';
|
||||||
|
EXPORTS : 'exports';
|
||||||
|
OPENS : 'opens';
|
||||||
|
TO : 'to';
|
||||||
|
USES : 'uses';
|
||||||
|
PROVIDES : 'provides';
|
||||||
|
WITH : 'with';
|
||||||
|
TRANSITIVE : 'transitive';
|
||||||
|
|
||||||
|
// Local Variable Type Inference
|
||||||
|
VAR: 'var'; // reserved type name
|
||||||
|
|
||||||
|
// Switch Expressions
|
||||||
|
YIELD: 'yield'; // reserved type name from Java 14
|
||||||
|
|
||||||
|
// Records
|
||||||
|
RECORD: 'record';
|
||||||
|
|
||||||
|
// Sealed Classes
|
||||||
|
SEALED : 'sealed';
|
||||||
|
PERMITS : 'permits';
|
||||||
|
NON_SEALED : 'non-sealed';
|
||||||
|
|
||||||
|
// Literals
|
||||||
|
|
||||||
|
DECIMAL_LITERAL : ('0' | [1-9] (Digits? | '_'+ Digits)) [lL]?;
|
||||||
|
HEX_LITERAL : '0' [xX] [0-9a-fA-F] ([0-9a-fA-F_]* [0-9a-fA-F])? [lL]?;
|
||||||
|
OCT_LITERAL : '0' '_'* [0-7] ([0-7_]* [0-7])? [lL]?;
|
||||||
|
BINARY_LITERAL : '0' [bB] [01] ([01_]* [01])? [lL]?;
|
||||||
|
|
||||||
|
FLOAT_LITERAL:
|
||||||
|
(Digits '.' Digits? | '.' Digits) ExponentPart? [fFdD]?
|
||||||
|
| Digits (ExponentPart [fFdD]? | [fFdD])
|
||||||
|
;
|
||||||
|
|
||||||
|
HEX_FLOAT_LITERAL: '0' [xX] (HexDigits '.'? | HexDigits? '.' HexDigits) [pP] [+-]? Digits [fFdD]?;
|
||||||
|
|
||||||
|
BOOL_LITERAL: 'true' | 'false';
|
||||||
|
|
||||||
|
CHAR_LITERAL: '\'' (~['\\\r\n] | EscapeSequence) '\'';
|
||||||
|
|
||||||
|
STRING_LITERAL: '"' (~["\\\r\n] | EscapeSequence)* '"';
|
||||||
|
|
||||||
|
TEXT_BLOCK: '"""' [ \t]* [\r\n] (. | EscapeSequence)*? '"""';
|
||||||
|
|
||||||
|
NULL_LITERAL: 'null';
|
||||||
|
|
||||||
|
// Separators
|
||||||
|
|
||||||
|
LPAREN : '(';
|
||||||
|
RPAREN : ')';
|
||||||
|
LBRACE : '{';
|
||||||
|
RBRACE : '}';
|
||||||
|
LBRACK : '[';
|
||||||
|
RBRACK : ']';
|
||||||
|
SEMI : ';';
|
||||||
|
COMMA : ',';
|
||||||
|
DOT : '.';
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
|
||||||
|
ASSIGN : '=';
|
||||||
|
GT : '>';
|
||||||
|
LT : '<';
|
||||||
|
BANG : '!';
|
||||||
|
TILDE : '~';
|
||||||
|
QUESTION : '?';
|
||||||
|
COLON : ':';
|
||||||
|
EQUAL : '==';
|
||||||
|
LE : '<=';
|
||||||
|
GE : '>=';
|
||||||
|
NOTEQUAL : '!=';
|
||||||
|
AND : '&&';
|
||||||
|
OR : '||';
|
||||||
|
INC : '++';
|
||||||
|
DEC : '--';
|
||||||
|
ADD : '+';
|
||||||
|
SUB : '-';
|
||||||
|
MUL : '*';
|
||||||
|
DIV : '/';
|
||||||
|
BITAND : '&';
|
||||||
|
BITOR : '|';
|
||||||
|
CARET : '^';
|
||||||
|
MOD : '%';
|
||||||
|
|
||||||
|
ADD_ASSIGN : '+=';
|
||||||
|
SUB_ASSIGN : '-=';
|
||||||
|
MUL_ASSIGN : '*=';
|
||||||
|
DIV_ASSIGN : '/=';
|
||||||
|
AND_ASSIGN : '&=';
|
||||||
|
OR_ASSIGN : '|=';
|
||||||
|
XOR_ASSIGN : '^=';
|
||||||
|
MOD_ASSIGN : '%=';
|
||||||
|
LSHIFT_ASSIGN : '<<=';
|
||||||
|
RSHIFT_ASSIGN : '>>=';
|
||||||
|
URSHIFT_ASSIGN : '>>>=';
|
||||||
|
|
||||||
|
// Java 8 tokens
|
||||||
|
|
||||||
|
ARROW : '->';
|
||||||
|
COLONCOLON : '::';
|
||||||
|
|
||||||
|
// Additional symbols not defined in the lexical specification
|
||||||
|
|
||||||
|
AT : '@';
|
||||||
|
ELLIPSIS : '...';
|
||||||
|
|
||||||
|
// Whitespace and comments
|
||||||
|
|
||||||
|
WS : [ \t\r\n\u000C]+ -> channel(HIDDEN);
|
||||||
|
COMMENT : '/*' .*? '*/' -> channel(HIDDEN);
|
||||||
|
LINE_COMMENT : '//' ~[\r\n]* -> channel(HIDDEN);
|
||||||
|
|
||||||
|
// Identifiers
|
||||||
|
|
||||||
|
IDENTIFIER: Letter LetterOrDigit*;
|
||||||
|
|
||||||
|
// Fragment rules
|
||||||
|
|
||||||
|
fragment ExponentPart: [eE] [+-]? Digits;
|
||||||
|
|
||||||
|
fragment EscapeSequence:
|
||||||
|
'\\' 'u005c'? [btnfr"'\\]
|
||||||
|
| '\\' 'u005c'? ([0-3]? [0-7])? [0-7]
|
||||||
|
| '\\' 'u'+ HexDigit HexDigit HexDigit HexDigit
|
||||||
|
;
|
||||||
|
|
||||||
|
fragment HexDigits: HexDigit ((HexDigit | '_')* HexDigit)?;
|
||||||
|
|
||||||
|
fragment HexDigit: [0-9a-fA-F];
|
||||||
|
|
||||||
|
fragment Digits: [0-9] ([0-9_]* [0-9])?;
|
||||||
|
|
||||||
|
fragment LetterOrDigit: Letter | [0-9];
|
||||||
|
|
||||||
|
fragment Letter:
|
||||||
|
[a-zA-Z$_] // these are the "java letters" below 0x7F
|
||||||
|
| ~[\u0000-\u007F\uD800-\uDBFF] // covers all characters above 0x7F which are not a surrogate
|
||||||
|
| [\uD800-\uDBFF] [\uDC00-\uDFFF] // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
|
||||||
|
;
|
813
src/antlr/JavaParser.g4
Normal file
813
src/antlr/JavaParser.g4
Normal file
|
@ -0,0 +1,813 @@
|
||||||
|
/*
|
||||||
|
[The "BSD licence"]
|
||||||
|
Copyright (c) 2013 Terence Parr, Sam Harwell
|
||||||
|
Copyright (c) 2017 Ivan Kochurkin (upgrade to Java 8)
|
||||||
|
Copyright (c) 2021 Michał Lorek (upgrade to Java 11)
|
||||||
|
Copyright (c) 2022 Michał Lorek (upgrade to Java 17)
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false
|
||||||
|
// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging
|
||||||
|
|
||||||
|
parser grammar JavaParser;
|
||||||
|
|
||||||
|
options {
|
||||||
|
tokenVocab = JavaLexer;
|
||||||
|
}
|
||||||
|
|
||||||
|
compilationUnit
|
||||||
|
: packageDeclaration? (importDeclaration | ';')* (typeDeclaration | ';')* EOF
|
||||||
|
| moduleDeclaration EOF
|
||||||
|
;
|
||||||
|
|
||||||
|
packageDeclaration
|
||||||
|
: annotation* PACKAGE qualifiedName ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
importDeclaration
|
||||||
|
: IMPORT STATIC? qualifiedName ('.' '*')? ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
typeDeclaration
|
||||||
|
: classOrInterfaceModifier* (
|
||||||
|
classDeclaration
|
||||||
|
| enumDeclaration
|
||||||
|
| interfaceDeclaration
|
||||||
|
| annotationTypeDeclaration
|
||||||
|
| recordDeclaration
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
modifier
|
||||||
|
: classOrInterfaceModifier
|
||||||
|
| NATIVE
|
||||||
|
| SYNCHRONIZED
|
||||||
|
| TRANSIENT
|
||||||
|
| VOLATILE
|
||||||
|
;
|
||||||
|
|
||||||
|
classOrInterfaceModifier
|
||||||
|
: annotation
|
||||||
|
| PUBLIC
|
||||||
|
| PROTECTED
|
||||||
|
| PRIVATE
|
||||||
|
| STATIC
|
||||||
|
| ABSTRACT
|
||||||
|
| FINAL // FINAL for class only -- does not apply to interfaces
|
||||||
|
| STRICTFP
|
||||||
|
| SEALED // Java17
|
||||||
|
| NON_SEALED // Java17
|
||||||
|
;
|
||||||
|
|
||||||
|
variableModifier
|
||||||
|
: FINAL
|
||||||
|
| annotation
|
||||||
|
;
|
||||||
|
|
||||||
|
classDeclaration
|
||||||
|
: CLASS identifier typeParameters? (EXTENDS typeType)? (IMPLEMENTS typeList)? (
|
||||||
|
PERMITS typeList
|
||||||
|
)? // Java17
|
||||||
|
classBody
|
||||||
|
;
|
||||||
|
|
||||||
|
typeParameters
|
||||||
|
: '<' typeParameter (',' typeParameter)* '>'
|
||||||
|
;
|
||||||
|
|
||||||
|
typeParameter
|
||||||
|
: annotation* identifier (EXTENDS annotation* typeBound)?
|
||||||
|
;
|
||||||
|
|
||||||
|
typeBound
|
||||||
|
: typeType ('&' typeType)*
|
||||||
|
;
|
||||||
|
|
||||||
|
enumDeclaration
|
||||||
|
: ENUM identifier (IMPLEMENTS typeList)? '{' enumConstants? ','? enumBodyDeclarations? '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
enumConstants
|
||||||
|
: enumConstant (',' enumConstant)*
|
||||||
|
;
|
||||||
|
|
||||||
|
enumConstant
|
||||||
|
: annotation* identifier arguments? classBody?
|
||||||
|
;
|
||||||
|
|
||||||
|
enumBodyDeclarations
|
||||||
|
: ';' classBodyDeclaration*
|
||||||
|
;
|
||||||
|
|
||||||
|
interfaceDeclaration
|
||||||
|
: INTERFACE identifier typeParameters? (EXTENDS typeList)? (PERMITS typeList)? interfaceBody
|
||||||
|
;
|
||||||
|
|
||||||
|
classBody
|
||||||
|
: '{' classBodyDeclaration* '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
interfaceBody
|
||||||
|
: '{' interfaceBodyDeclaration* '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
classBodyDeclaration
|
||||||
|
: ';'
|
||||||
|
| STATIC? block
|
||||||
|
| modifier* memberDeclaration
|
||||||
|
;
|
||||||
|
|
||||||
|
memberDeclaration
|
||||||
|
: recordDeclaration //Java17
|
||||||
|
| methodDeclaration
|
||||||
|
| genericMethodDeclaration
|
||||||
|
| fieldDeclaration
|
||||||
|
| constructorDeclaration
|
||||||
|
| genericConstructorDeclaration
|
||||||
|
| interfaceDeclaration
|
||||||
|
| annotationTypeDeclaration
|
||||||
|
| classDeclaration
|
||||||
|
| enumDeclaration
|
||||||
|
;
|
||||||
|
|
||||||
|
/* We use rule this even for void methods which cannot have [] after parameters.
|
||||||
|
This simplifies grammar and we can consider void to be a type, which
|
||||||
|
renders the [] matching as a context-sensitive issue or a semantic check
|
||||||
|
for invalid return type after parsing.
|
||||||
|
*/
|
||||||
|
methodDeclaration
|
||||||
|
: typeTypeOrVoid identifier formalParameters ('[' ']')* (THROWS qualifiedNameList)? methodBody
|
||||||
|
;
|
||||||
|
|
||||||
|
methodBody
|
||||||
|
: block
|
||||||
|
| ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
typeTypeOrVoid
|
||||||
|
: typeType
|
||||||
|
| VOID
|
||||||
|
;
|
||||||
|
|
||||||
|
genericMethodDeclaration
|
||||||
|
: typeParameters methodDeclaration
|
||||||
|
;
|
||||||
|
|
||||||
|
genericConstructorDeclaration
|
||||||
|
: typeParameters constructorDeclaration
|
||||||
|
;
|
||||||
|
|
||||||
|
constructorDeclaration
|
||||||
|
: identifier formalParameters (THROWS qualifiedNameList)? constructorBody = block
|
||||||
|
;
|
||||||
|
|
||||||
|
compactConstructorDeclaration
|
||||||
|
: modifier* identifier constructorBody = block
|
||||||
|
;
|
||||||
|
|
||||||
|
fieldDeclaration
|
||||||
|
: typeType variableDeclarators ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
interfaceBodyDeclaration
|
||||||
|
: modifier* interfaceMemberDeclaration
|
||||||
|
| ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
interfaceMemberDeclaration
|
||||||
|
: recordDeclaration // Java17
|
||||||
|
| constDeclaration
|
||||||
|
| interfaceMethodDeclaration
|
||||||
|
| genericInterfaceMethodDeclaration
|
||||||
|
| interfaceDeclaration
|
||||||
|
| annotationTypeDeclaration
|
||||||
|
| classDeclaration
|
||||||
|
| enumDeclaration
|
||||||
|
;
|
||||||
|
|
||||||
|
constDeclaration
|
||||||
|
: typeType constantDeclarator (',' constantDeclarator)* ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
constantDeclarator
|
||||||
|
: identifier ('[' ']')* '=' variableInitializer
|
||||||
|
;
|
||||||
|
|
||||||
|
// Early versions of Java allows brackets after the method name, eg.
|
||||||
|
// public int[] return2DArray() [] { ... }
|
||||||
|
// is the same as
|
||||||
|
// public int[][] return2DArray() { ... }
|
||||||
|
interfaceMethodDeclaration
|
||||||
|
: interfaceMethodModifier* interfaceCommonBodyDeclaration
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java8
|
||||||
|
interfaceMethodModifier
|
||||||
|
: annotation
|
||||||
|
| PUBLIC
|
||||||
|
| ABSTRACT
|
||||||
|
| DEFAULT
|
||||||
|
| STATIC
|
||||||
|
| STRICTFP
|
||||||
|
;
|
||||||
|
|
||||||
|
genericInterfaceMethodDeclaration
|
||||||
|
: interfaceMethodModifier* typeParameters interfaceCommonBodyDeclaration
|
||||||
|
;
|
||||||
|
|
||||||
|
interfaceCommonBodyDeclaration
|
||||||
|
: annotation* typeTypeOrVoid identifier formalParameters ('[' ']')* (THROWS qualifiedNameList)? methodBody
|
||||||
|
;
|
||||||
|
|
||||||
|
variableDeclarators
|
||||||
|
: variableDeclarator (',' variableDeclarator)*
|
||||||
|
;
|
||||||
|
|
||||||
|
variableDeclarator
|
||||||
|
: variableDeclaratorId ('=' variableInitializer)?
|
||||||
|
;
|
||||||
|
|
||||||
|
variableDeclaratorId
|
||||||
|
: identifier ('[' ']')*
|
||||||
|
;
|
||||||
|
|
||||||
|
variableInitializer
|
||||||
|
: arrayInitializer
|
||||||
|
| expression
|
||||||
|
;
|
||||||
|
|
||||||
|
arrayInitializer
|
||||||
|
: '{' (variableInitializer (',' variableInitializer)* ','?)? '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
classOrInterfaceType
|
||||||
|
: (identifier typeArguments? '.')* typeIdentifier typeArguments?
|
||||||
|
;
|
||||||
|
|
||||||
|
typeArgument
|
||||||
|
: typeType
|
||||||
|
| annotation* '?' ((EXTENDS | SUPER) typeType)?
|
||||||
|
;
|
||||||
|
|
||||||
|
qualifiedNameList
|
||||||
|
: qualifiedName (',' qualifiedName)*
|
||||||
|
;
|
||||||
|
|
||||||
|
formalParameters
|
||||||
|
: '(' (
|
||||||
|
receiverParameter?
|
||||||
|
| receiverParameter (',' formalParameterList)?
|
||||||
|
| formalParameterList?
|
||||||
|
) ')'
|
||||||
|
;
|
||||||
|
|
||||||
|
receiverParameter
|
||||||
|
: typeType (identifier '.')* THIS
|
||||||
|
;
|
||||||
|
|
||||||
|
formalParameterList
|
||||||
|
: formalParameter (',' formalParameter)* (',' lastFormalParameter)?
|
||||||
|
| lastFormalParameter
|
||||||
|
;
|
||||||
|
|
||||||
|
formalParameter
|
||||||
|
: variableModifier* typeType variableDeclaratorId
|
||||||
|
;
|
||||||
|
|
||||||
|
lastFormalParameter
|
||||||
|
: variableModifier* typeType annotation* '...' variableDeclaratorId
|
||||||
|
;
|
||||||
|
|
||||||
|
// local variable type inference
|
||||||
|
lambdaLVTIList
|
||||||
|
: lambdaLVTIParameter (',' lambdaLVTIParameter)*
|
||||||
|
;
|
||||||
|
|
||||||
|
lambdaLVTIParameter
|
||||||
|
: variableModifier* VAR identifier
|
||||||
|
;
|
||||||
|
|
||||||
|
qualifiedName
|
||||||
|
: identifier ('.' identifier)*
|
||||||
|
;
|
||||||
|
|
||||||
|
literal
|
||||||
|
: integerLiteral
|
||||||
|
| floatLiteral
|
||||||
|
| CHAR_LITERAL
|
||||||
|
| STRING_LITERAL
|
||||||
|
| BOOL_LITERAL
|
||||||
|
| NULL_LITERAL
|
||||||
|
| TEXT_BLOCK // Java17
|
||||||
|
;
|
||||||
|
|
||||||
|
integerLiteral
|
||||||
|
: DECIMAL_LITERAL
|
||||||
|
| HEX_LITERAL
|
||||||
|
| OCT_LITERAL
|
||||||
|
| BINARY_LITERAL
|
||||||
|
;
|
||||||
|
|
||||||
|
floatLiteral
|
||||||
|
: FLOAT_LITERAL
|
||||||
|
| HEX_FLOAT_LITERAL
|
||||||
|
;
|
||||||
|
|
||||||
|
// ANNOTATIONS
|
||||||
|
altAnnotationQualifiedName
|
||||||
|
: (identifier DOT)* '@' identifier
|
||||||
|
;
|
||||||
|
|
||||||
|
annotation
|
||||||
|
: ('@' qualifiedName | altAnnotationQualifiedName) (
|
||||||
|
'(' ( elementValuePairs | elementValue)? ')'
|
||||||
|
)?
|
||||||
|
;
|
||||||
|
|
||||||
|
elementValuePairs
|
||||||
|
: elementValuePair (',' elementValuePair)*
|
||||||
|
;
|
||||||
|
|
||||||
|
elementValuePair
|
||||||
|
: identifier '=' elementValue
|
||||||
|
;
|
||||||
|
|
||||||
|
elementValue
|
||||||
|
: expression
|
||||||
|
| annotation
|
||||||
|
| elementValueArrayInitializer
|
||||||
|
;
|
||||||
|
|
||||||
|
elementValueArrayInitializer
|
||||||
|
: '{' (elementValue (',' elementValue)*)? ','? '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
annotationTypeDeclaration
|
||||||
|
: '@' INTERFACE identifier annotationTypeBody
|
||||||
|
;
|
||||||
|
|
||||||
|
annotationTypeBody
|
||||||
|
: '{' annotationTypeElementDeclaration* '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
annotationTypeElementDeclaration
|
||||||
|
: modifier* annotationTypeElementRest
|
||||||
|
| ';' // this is not allowed by the grammar, but apparently allowed by the actual compiler
|
||||||
|
;
|
||||||
|
|
||||||
|
annotationTypeElementRest
|
||||||
|
: typeType annotationMethodOrConstantRest ';'
|
||||||
|
| classDeclaration ';'?
|
||||||
|
| interfaceDeclaration ';'?
|
||||||
|
| enumDeclaration ';'?
|
||||||
|
| annotationTypeDeclaration ';'?
|
||||||
|
| recordDeclaration ';'? // Java17
|
||||||
|
;
|
||||||
|
|
||||||
|
annotationMethodOrConstantRest
|
||||||
|
: annotationMethodRest
|
||||||
|
| annotationConstantRest
|
||||||
|
;
|
||||||
|
|
||||||
|
annotationMethodRest
|
||||||
|
: identifier '(' ')' defaultValue?
|
||||||
|
;
|
||||||
|
|
||||||
|
annotationConstantRest
|
||||||
|
: variableDeclarators
|
||||||
|
;
|
||||||
|
|
||||||
|
defaultValue
|
||||||
|
: DEFAULT elementValue
|
||||||
|
;
|
||||||
|
|
||||||
|
// MODULES - Java9
|
||||||
|
|
||||||
|
moduleDeclaration
|
||||||
|
: OPEN? MODULE qualifiedName moduleBody
|
||||||
|
;
|
||||||
|
|
||||||
|
moduleBody
|
||||||
|
: '{' moduleDirective* '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
moduleDirective
|
||||||
|
: REQUIRES requiresModifier* qualifiedName ';'
|
||||||
|
| EXPORTS qualifiedName (TO qualifiedName)? ';'
|
||||||
|
| OPENS qualifiedName (TO qualifiedName)? ';'
|
||||||
|
| USES qualifiedName ';'
|
||||||
|
| PROVIDES qualifiedName WITH qualifiedName ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
requiresModifier
|
||||||
|
: TRANSITIVE
|
||||||
|
| STATIC
|
||||||
|
;
|
||||||
|
|
||||||
|
// RECORDS - Java 17
|
||||||
|
|
||||||
|
recordDeclaration
|
||||||
|
: RECORD identifier typeParameters? recordHeader (IMPLEMENTS typeList)? recordBody
|
||||||
|
;
|
||||||
|
|
||||||
|
recordHeader
|
||||||
|
: '(' recordComponentList? ')'
|
||||||
|
;
|
||||||
|
|
||||||
|
recordComponentList
|
||||||
|
: recordComponent (',' recordComponent)*
|
||||||
|
;
|
||||||
|
|
||||||
|
recordComponent
|
||||||
|
: typeType identifier
|
||||||
|
;
|
||||||
|
|
||||||
|
recordBody
|
||||||
|
: '{' (classBodyDeclaration | compactConstructorDeclaration)* '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
// STATEMENTS / BLOCKS
|
||||||
|
|
||||||
|
block
|
||||||
|
: '{' blockStatement* '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
blockStatement
|
||||||
|
: localVariableDeclaration ';'
|
||||||
|
| localTypeDeclaration
|
||||||
|
| statement
|
||||||
|
;
|
||||||
|
|
||||||
|
localVariableDeclaration
|
||||||
|
: variableModifier* (VAR identifier '=' expression | typeType variableDeclarators)
|
||||||
|
;
|
||||||
|
|
||||||
|
identifier
|
||||||
|
: IDENTIFIER
|
||||||
|
| MODULE
|
||||||
|
| OPEN
|
||||||
|
| REQUIRES
|
||||||
|
| EXPORTS
|
||||||
|
| OPENS
|
||||||
|
| TO
|
||||||
|
| USES
|
||||||
|
| PROVIDES
|
||||||
|
| WITH
|
||||||
|
| TRANSITIVE
|
||||||
|
| YIELD
|
||||||
|
| SEALED
|
||||||
|
| PERMITS
|
||||||
|
| RECORD
|
||||||
|
| VAR
|
||||||
|
;
|
||||||
|
|
||||||
|
typeIdentifier // Identifiers that are not restricted for type declarations
|
||||||
|
: IDENTIFIER
|
||||||
|
| MODULE
|
||||||
|
| OPEN
|
||||||
|
| REQUIRES
|
||||||
|
| EXPORTS
|
||||||
|
| OPENS
|
||||||
|
| TO
|
||||||
|
| USES
|
||||||
|
| PROVIDES
|
||||||
|
| WITH
|
||||||
|
| TRANSITIVE
|
||||||
|
| SEALED
|
||||||
|
| PERMITS
|
||||||
|
| RECORD
|
||||||
|
;
|
||||||
|
|
||||||
|
localTypeDeclaration
|
||||||
|
: classOrInterfaceModifier* (classDeclaration | interfaceDeclaration | recordDeclaration)
|
||||||
|
;
|
||||||
|
|
||||||
|
statement
|
||||||
|
: blockLabel = block
|
||||||
|
| ASSERT expression (':' expression)? ';'
|
||||||
|
| IF parExpression statement (ELSE statement)?
|
||||||
|
| FOR '(' forControl ')' statement
|
||||||
|
| WHILE parExpression statement
|
||||||
|
| DO statement WHILE parExpression ';'
|
||||||
|
| TRY block (catchClause+ finallyBlock? | finallyBlock)
|
||||||
|
| TRY resourceSpecification block catchClause* finallyBlock?
|
||||||
|
| SWITCH parExpression '{' switchBlockStatementGroup* switchLabel* '}'
|
||||||
|
| SYNCHRONIZED parExpression block
|
||||||
|
| RETURN expression? ';'
|
||||||
|
| THROW expression ';'
|
||||||
|
| BREAK identifier? ';'
|
||||||
|
| CONTINUE identifier? ';'
|
||||||
|
| YIELD expression ';' // Java17
|
||||||
|
| SEMI
|
||||||
|
| statementExpression = expression ';'
|
||||||
|
| switchExpression ';'? // Java17
|
||||||
|
| identifierLabel = identifier ':' statement
|
||||||
|
;
|
||||||
|
|
||||||
|
catchClause
|
||||||
|
: CATCH '(' variableModifier* catchType identifier ')' block
|
||||||
|
;
|
||||||
|
|
||||||
|
catchType
|
||||||
|
: qualifiedName ('|' qualifiedName)*
|
||||||
|
;
|
||||||
|
|
||||||
|
finallyBlock
|
||||||
|
: FINALLY block
|
||||||
|
;
|
||||||
|
|
||||||
|
resourceSpecification
|
||||||
|
: '(' resources ';'? ')'
|
||||||
|
;
|
||||||
|
|
||||||
|
resources
|
||||||
|
: resource (';' resource)*
|
||||||
|
;
|
||||||
|
|
||||||
|
resource
|
||||||
|
: variableModifier* (classOrInterfaceType variableDeclaratorId | VAR identifier) '=' expression
|
||||||
|
| qualifiedName
|
||||||
|
;
|
||||||
|
|
||||||
|
/** Matches cases then statements, both of which are mandatory.
|
||||||
|
* To handle empty cases at the end, we add switchLabel* to statement.
|
||||||
|
*/
|
||||||
|
switchBlockStatementGroup
|
||||||
|
: switchLabel+ blockStatement+
|
||||||
|
;
|
||||||
|
|
||||||
|
switchLabel
|
||||||
|
: CASE (
|
||||||
|
constantExpression = expression
|
||||||
|
| enumConstantName = IDENTIFIER
|
||||||
|
| typeType varName = identifier
|
||||||
|
) ':'
|
||||||
|
| DEFAULT ':'
|
||||||
|
;
|
||||||
|
|
||||||
|
forControl
|
||||||
|
: enhancedForControl
|
||||||
|
| forInit? ';' expression? ';' forUpdate = expressionList?
|
||||||
|
;
|
||||||
|
|
||||||
|
forInit
|
||||||
|
: localVariableDeclaration
|
||||||
|
| expressionList
|
||||||
|
;
|
||||||
|
|
||||||
|
enhancedForControl
|
||||||
|
: variableModifier* (typeType | VAR) variableDeclaratorId ':' expression
|
||||||
|
;
|
||||||
|
|
||||||
|
// EXPRESSIONS
|
||||||
|
|
||||||
|
parExpression
|
||||||
|
: '(' expression ')'
|
||||||
|
;
|
||||||
|
|
||||||
|
expressionList
|
||||||
|
: expression (',' expression)*
|
||||||
|
;
|
||||||
|
|
||||||
|
methodCall
|
||||||
|
: (identifier | THIS | SUPER) arguments
|
||||||
|
;
|
||||||
|
|
||||||
|
expression
|
||||||
|
// Expression order in accordance with https://introcs.cs.princeton.edu/java/11precedence/
|
||||||
|
// Level 16, Primary, array and member access
|
||||||
|
: primary #PrimaryExpression
|
||||||
|
| expression '[' expression ']' #SquareBracketExpression
|
||||||
|
| expression bop = '.' (
|
||||||
|
identifier
|
||||||
|
| methodCall
|
||||||
|
| THIS
|
||||||
|
| NEW nonWildcardTypeArguments? innerCreator
|
||||||
|
| SUPER superSuffix
|
||||||
|
| explicitGenericInvocation
|
||||||
|
) #MemberReferenceExpression
|
||||||
|
// Method calls and method references are part of primary, and hence level 16 precedence
|
||||||
|
| methodCall #MethodCallExpression
|
||||||
|
| expression '::' typeArguments? identifier #MethodReferenceExpression
|
||||||
|
| typeType '::' (typeArguments? identifier | NEW) #MethodReferenceExpression
|
||||||
|
| classType '::' typeArguments? NEW #MethodReferenceExpression
|
||||||
|
|
||||||
|
// Java17
|
||||||
|
| switchExpression #ExpressionSwitch
|
||||||
|
|
||||||
|
// Level 15 Post-increment/decrement operators
|
||||||
|
| expression postfix = ('++' | '--') #PostIncrementDecrementOperatorExpression
|
||||||
|
|
||||||
|
// Level 14, Unary operators
|
||||||
|
| prefix = ('+' | '-' | '++' | '--' | '~' | '!') expression #UnaryOperatorExpression
|
||||||
|
|
||||||
|
// Level 13 Cast and object creation
|
||||||
|
| '(' annotation* typeType ('&' typeType)* ')' expression #CastExpression
|
||||||
|
| NEW creator #ObjectCreationExpression
|
||||||
|
|
||||||
|
// Level 12 to 1, Remaining operators
|
||||||
|
// Level 12, Multiplicative operators
|
||||||
|
| expression bop = ('*' | '/' | '%') expression #BinaryOperatorExpression
|
||||||
|
// Level 11, Additive operators
|
||||||
|
| expression bop = ('+' | '-') expression #BinaryOperatorExpression
|
||||||
|
// Level 10, Shift operators
|
||||||
|
| expression ('<' '<' | '>' '>' '>' | '>' '>') expression #BinaryOperatorExpression
|
||||||
|
// Level 9, Relational operators
|
||||||
|
| expression bop = ('<=' | '>=' | '>' | '<') expression #BinaryOperatorExpression
|
||||||
|
| expression bop = INSTANCEOF (typeType | pattern) #InstanceOfOperatorExpression
|
||||||
|
// Level 8, Equality Operators
|
||||||
|
| expression bop = ('==' | '!=') expression #BinaryOperatorExpression
|
||||||
|
// Level 7, Bitwise AND
|
||||||
|
| expression bop = '&' expression #BinaryOperatorExpression
|
||||||
|
// Level 6, Bitwise XOR
|
||||||
|
| expression bop = '^' expression #BinaryOperatorExpression
|
||||||
|
// Level 5, Bitwise OR
|
||||||
|
| expression bop = '|' expression #BinaryOperatorExpression
|
||||||
|
// Level 4, Logic AND
|
||||||
|
| expression bop = '&&' expression #BinaryOperatorExpression
|
||||||
|
// Level 3, Logic OR
|
||||||
|
| expression bop = '||' expression #BinaryOperatorExpression
|
||||||
|
// Level 2, Ternary
|
||||||
|
| <assoc = right> expression bop = '?' expression ':' expression #TernaryExpression
|
||||||
|
// Level 1, Assignment
|
||||||
|
| <assoc = right> expression bop = (
|
||||||
|
'='
|
||||||
|
| '+='
|
||||||
|
| '-='
|
||||||
|
| '*='
|
||||||
|
| '/='
|
||||||
|
| '&='
|
||||||
|
| '|='
|
||||||
|
| '^='
|
||||||
|
| '>>='
|
||||||
|
| '>>>='
|
||||||
|
| '<<='
|
||||||
|
| '%='
|
||||||
|
) expression #BinaryOperatorExpression
|
||||||
|
|
||||||
|
// Level 0, Lambda Expression // Java8
|
||||||
|
| lambdaExpression #ExpressionLambda
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java17
|
||||||
|
pattern
|
||||||
|
: variableModifier* typeType annotation* identifier
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java8
|
||||||
|
lambdaExpression
|
||||||
|
: lambdaParameters '->' lambdaBody
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java8
|
||||||
|
lambdaParameters
|
||||||
|
: identifier
|
||||||
|
| '(' formalParameterList? ')'
|
||||||
|
| '(' identifier (',' identifier)* ')'
|
||||||
|
| '(' lambdaLVTIList? ')'
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java8
|
||||||
|
lambdaBody
|
||||||
|
: expression
|
||||||
|
| block
|
||||||
|
;
|
||||||
|
|
||||||
|
primary
|
||||||
|
: '(' expression ')'
|
||||||
|
| THIS
|
||||||
|
| SUPER
|
||||||
|
| literal
|
||||||
|
| identifier
|
||||||
|
| typeTypeOrVoid '.' CLASS
|
||||||
|
| nonWildcardTypeArguments (explicitGenericInvocationSuffix | THIS arguments)
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java17
|
||||||
|
switchExpression
|
||||||
|
: SWITCH parExpression '{' switchLabeledRule* '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java17
|
||||||
|
switchLabeledRule
|
||||||
|
: CASE (expressionList | NULL_LITERAL | guardedPattern) (ARROW | COLON) switchRuleOutcome
|
||||||
|
| DEFAULT (ARROW | COLON) switchRuleOutcome
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java17
|
||||||
|
guardedPattern
|
||||||
|
: '(' guardedPattern ')'
|
||||||
|
| variableModifier* typeType annotation* identifier ('&&' expression)*
|
||||||
|
| guardedPattern '&&' expression
|
||||||
|
;
|
||||||
|
|
||||||
|
// Java17
|
||||||
|
switchRuleOutcome
|
||||||
|
: block
|
||||||
|
| blockStatement*
|
||||||
|
;
|
||||||
|
|
||||||
|
classType
|
||||||
|
: (classOrInterfaceType '.')? annotation* identifier typeArguments?
|
||||||
|
;
|
||||||
|
|
||||||
|
creator
|
||||||
|
: nonWildcardTypeArguments? createdName classCreatorRest
|
||||||
|
| createdName arrayCreatorRest
|
||||||
|
;
|
||||||
|
|
||||||
|
createdName
|
||||||
|
: identifier typeArgumentsOrDiamond? ('.' identifier typeArgumentsOrDiamond?)*
|
||||||
|
| primitiveType
|
||||||
|
;
|
||||||
|
|
||||||
|
innerCreator
|
||||||
|
: identifier nonWildcardTypeArgumentsOrDiamond? classCreatorRest
|
||||||
|
;
|
||||||
|
|
||||||
|
arrayCreatorRest
|
||||||
|
: ('[' ']')+ arrayInitializer
|
||||||
|
| ('[' expression ']')+ ('[' ']')*
|
||||||
|
;
|
||||||
|
|
||||||
|
classCreatorRest
|
||||||
|
: arguments classBody?
|
||||||
|
;
|
||||||
|
|
||||||
|
explicitGenericInvocation
|
||||||
|
: nonWildcardTypeArguments explicitGenericInvocationSuffix
|
||||||
|
;
|
||||||
|
|
||||||
|
typeArgumentsOrDiamond
|
||||||
|
: '<' '>'
|
||||||
|
| typeArguments
|
||||||
|
;
|
||||||
|
|
||||||
|
nonWildcardTypeArgumentsOrDiamond
|
||||||
|
: '<' '>'
|
||||||
|
| nonWildcardTypeArguments
|
||||||
|
;
|
||||||
|
|
||||||
|
nonWildcardTypeArguments
|
||||||
|
: '<' typeList '>'
|
||||||
|
;
|
||||||
|
|
||||||
|
typeList
|
||||||
|
: typeType (',' typeType)*
|
||||||
|
;
|
||||||
|
|
||||||
|
typeType
|
||||||
|
: annotation* (classOrInterfaceType | primitiveType) (annotation* '[' ']')*
|
||||||
|
;
|
||||||
|
|
||||||
|
primitiveType
|
||||||
|
: BOOLEAN
|
||||||
|
| CHAR
|
||||||
|
| BYTE
|
||||||
|
| SHORT
|
||||||
|
| INT
|
||||||
|
| LONG
|
||||||
|
| FLOAT
|
||||||
|
| DOUBLE
|
||||||
|
;
|
||||||
|
|
||||||
|
typeArguments
|
||||||
|
: '<' typeArgument (',' typeArgument)* '>'
|
||||||
|
;
|
||||||
|
|
||||||
|
superSuffix
|
||||||
|
: arguments
|
||||||
|
| '.' typeArguments? identifier arguments?
|
||||||
|
;
|
||||||
|
|
||||||
|
explicitGenericInvocationSuffix
|
||||||
|
: SUPER superSuffix
|
||||||
|
| identifier arguments
|
||||||
|
;
|
||||||
|
|
||||||
|
arguments
|
||||||
|
: '(' expressionList? ')'
|
||||||
|
;
|
0
src/main/fonts/montserrat-webfont.svg
Normal file
0
src/main/fonts/montserrat-webfont.svg
Normal file
0
src/main/fonts/varela_round-webfont.svg
Normal file
0
src/main/fonts/varela_round-webfont.svg
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Customer {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
private String location; // Changed from 'city'
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocation() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocation(String location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
public interface CustomerRepository extends JpaRepository<Customer, Integer> {
|
||||||
|
|
||||||
|
// Find customers by last name with pagination
|
||||||
|
Page<Customer> findByLastNameStartingWith(String lastName, Pageable pageable);
|
||||||
|
|
||||||
|
// Custom query to filter by last name and location
|
||||||
|
@Query("SELECT c FROM Customer c WHERE c.lastName LIKE :lastName% AND (:location IS NULL OR c.location = :location)")
|
||||||
|
Page<Customer> findByLastNameAndLocation(@Param("lastName") String lastName, @Param("location") String location,
|
||||||
|
Pageable pageable);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CustomerService {
|
||||||
|
|
||||||
|
private final CustomerRepository customerRepository;
|
||||||
|
|
||||||
|
public CustomerService(CustomerRepository customerRepository) {
|
||||||
|
this.customerRepository = customerRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<Customer> findCustomers(String lastName, String location, int page) {
|
||||||
|
Pageable pageable = PageRequest.of(page - 1, 5);
|
||||||
|
|
||||||
|
if (location == null || location.isEmpty()) {
|
||||||
|
return customerRepository.findByLastNameStartingWith(lastName, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return customerRepository.findByLastNameAndLocation(lastName, location, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,18 +1,3 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 the original author or authors.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* https://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.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -24,6 +9,7 @@ import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.validation.Validator;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
|
@ -32,17 +18,11 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
import org.springframework.samples.petclinic.util.DependencyLogger;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Juergen Hoeller
|
|
||||||
* @author Ken Krebs
|
|
||||||
* @author Arjen Poutsma
|
|
||||||
* @author Michael Isvy
|
|
||||||
* @author Wick Dynex
|
|
||||||
*/
|
|
||||||
@Controller
|
@Controller
|
||||||
class OwnerController {
|
class OwnerController {
|
||||||
|
|
||||||
|
@ -50,17 +30,33 @@ class OwnerController {
|
||||||
|
|
||||||
private final OwnerRepository owners;
|
private final OwnerRepository owners;
|
||||||
|
|
||||||
public OwnerController(OwnerRepository owners) {
|
private final OwnerService ownerService;
|
||||||
|
|
||||||
|
public OwnerController(OwnerRepository owners, OwnerService ownerService) {
|
||||||
this.owners = owners;
|
this.owners = owners;
|
||||||
|
this.ownerService = ownerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@InitBinder
|
@InitBinder
|
||||||
public void setAllowedFields(WebDataBinder dataBinder) {
|
public void initOwnerBinder(WebDataBinder dataBinder) {
|
||||||
|
DependencyLogger.log("Initializing data binder in OwnerController");
|
||||||
|
setAllowedFields(dataBinder);
|
||||||
|
addOwnerValidator(dataBinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAllowedFields(WebDataBinder dataBinder) {
|
||||||
|
DependencyLogger.log("Setting disallowed fields for WebDataBinder");
|
||||||
dataBinder.setDisallowedFields("id");
|
dataBinder.setDisallowedFields("id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addOwnerValidator(WebDataBinder dataBinder) {
|
||||||
|
DependencyLogger.log("Adding OwnerValidator to WebDataBinder");
|
||||||
|
dataBinder.addValidators((Validator) new OwnerValidator());
|
||||||
|
}
|
||||||
|
|
||||||
@ModelAttribute("owner")
|
@ModelAttribute("owner")
|
||||||
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
||||||
|
DependencyLogger.log("findOwner called with ownerId: " + ownerId);
|
||||||
return ownerId == null ? new Owner()
|
return ownerId == null ? new Owner()
|
||||||
: this.owners.findById(ownerId)
|
: this.owners.findById(ownerId)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId
|
.orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId
|
||||||
|
@ -69,53 +65,60 @@ class OwnerController {
|
||||||
|
|
||||||
@GetMapping("/owners/new")
|
@GetMapping("/owners/new")
|
||||||
public String initCreationForm() {
|
public String initCreationForm() {
|
||||||
|
DependencyLogger.log("Initializing creation form for new owner");
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/owners/new")
|
@PostMapping("/owners/new")
|
||||||
public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) {
|
public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) {
|
||||||
|
DependencyLogger.log("Processing creation form for new owner");
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
|
DependencyLogger.log("Validation errors occurred while creating owner");
|
||||||
redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner.");
|
redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner.");
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
DependencyLogger.log("New owner saved with ID: " + owner.getId());
|
||||||
redirectAttributes.addFlashAttribute("message", "New Owner Created");
|
redirectAttributes.addFlashAttribute("message", "New Owner Created");
|
||||||
return "redirect:/owners/" + owner.getId();
|
return "redirect:/owners/" + owner.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/owners/find")
|
@GetMapping("/owners/find")
|
||||||
public String initFindForm() {
|
public String initFindForm() {
|
||||||
|
DependencyLogger.log("Initializing find form for owners");
|
||||||
return "owners/findOwners";
|
return "owners/findOwners";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/owners")
|
@GetMapping("/owners")
|
||||||
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
|
public String processFindForm(@RequestParam(defaultValue = "1") int page,
|
||||||
Model model) {
|
@RequestParam(required = false) String city, Owner owner, BindingResult result, Model model) {
|
||||||
// allow parameterless GET request for /owners to return all records
|
DependencyLogger.log("Processing find form for owners, page: " + page + ", city: " + city);
|
||||||
|
|
||||||
if (owner.getLastName() == null) {
|
if (owner.getLastName() == null) {
|
||||||
owner.setLastName(""); // empty string signifies broadest possible search
|
owner.setLastName(""); // Broadest possible search
|
||||||
}
|
}
|
||||||
|
|
||||||
// find owners by last name
|
// Use ownerService to fetch paginated results
|
||||||
Page<Owner> ownersResults = findPaginatedForOwnersLastName(page, owner.getLastName());
|
Page<Owner> ownersResults = ownerService.findOwners(owner.getLastName(), city, page);
|
||||||
|
|
||||||
if (ownersResults.isEmpty()) {
|
if (ownersResults.isEmpty()) {
|
||||||
// no owners found
|
DependencyLogger.log("No owners found for last name: " + owner.getLastName());
|
||||||
result.rejectValue("lastName", "notFound", "not found");
|
result.rejectValue("lastName", "notFound", "not found");
|
||||||
return "owners/findOwners";
|
return "owners/findOwners";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ownersResults.getTotalElements() == 1) {
|
if (ownersResults.getTotalElements() == 1) {
|
||||||
// 1 owner found
|
|
||||||
owner = ownersResults.iterator().next();
|
owner = ownersResults.iterator().next();
|
||||||
return "redirect:/owners/" + owner.getId();
|
return "redirect:/owners/" + owner.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiple owners found
|
DependencyLogger.log("Multiple owners found, adding pagination model.");
|
||||||
return addPaginationModel(page, model, ownersResults);
|
return addPaginationModel(page, model, ownersResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String addPaginationModel(int page, Model model, Page<Owner> paginated) {
|
private String addPaginationModel(int page, Model model, Page<Owner> paginated) {
|
||||||
|
DependencyLogger.log("Adding pagination model for page: " + page);
|
||||||
List<Owner> listOwners = paginated.getContent();
|
List<Owner> listOwners = paginated.getContent();
|
||||||
model.addAttribute("currentPage", page);
|
model.addAttribute("currentPage", page);
|
||||||
model.addAttribute("totalPages", paginated.getTotalPages());
|
model.addAttribute("totalPages", paginated.getTotalPages());
|
||||||
|
@ -125,25 +128,31 @@ class OwnerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
||||||
|
DependencyLogger.log("Finding paginated owners for last name: " + lastname + ", page: " + page);
|
||||||
int pageSize = 5;
|
int pageSize = 5;
|
||||||
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
||||||
return owners.findByLastNameStartingWith(lastname, pageable);
|
return owners.findByLastNameStartingWith(lastname, pageable);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/owners/{ownerId}/edit")
|
@GetMapping("/owners/{ownerId}/edit")
|
||||||
public String initUpdateOwnerForm() {
|
public String initUpdateOwnerForm() {
|
||||||
|
DependencyLogger.log("Initializing update form for owner");
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/owners/{ownerId}/edit")
|
@PostMapping("/owners/{ownerId}/edit")
|
||||||
public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId,
|
public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId,
|
||||||
RedirectAttributes redirectAttributes) {
|
RedirectAttributes redirectAttributes) {
|
||||||
|
DependencyLogger.log("Processing update form for owner with ID: " + ownerId);
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
|
DependencyLogger.log("Validation errors occurred while updating owner with ID: " + ownerId);
|
||||||
redirectAttributes.addFlashAttribute("error", "There was an error in updating the owner.");
|
redirectAttributes.addFlashAttribute("error", "There was an error in updating the owner.");
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (owner.getId() != ownerId) {
|
if (owner.getId() != ownerId) {
|
||||||
|
DependencyLogger.log("Owner ID mismatch during update. Form ID: " + owner.getId() + ", URL ID: " + ownerId);
|
||||||
result.rejectValue("id", "mismatch", "The owner ID in the form does not match the URL.");
|
result.rejectValue("id", "mismatch", "The owner ID in the form does not match the URL.");
|
||||||
redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again.");
|
redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again.");
|
||||||
return "redirect:/owners/{ownerId}/edit";
|
return "redirect:/owners/{ownerId}/edit";
|
||||||
|
@ -151,17 +160,14 @@ class OwnerController {
|
||||||
|
|
||||||
owner.setId(ownerId);
|
owner.setId(ownerId);
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
DependencyLogger.log("Owner updated with ID: " + ownerId);
|
||||||
redirectAttributes.addFlashAttribute("message", "Owner Values Updated");
|
redirectAttributes.addFlashAttribute("message", "Owner Values Updated");
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom handler for displaying an owner.
|
|
||||||
* @param ownerId the ID of the owner to display
|
|
||||||
* @return a ModelMap with the model attributes for the view
|
|
||||||
*/
|
|
||||||
@GetMapping("/owners/{ownerId}")
|
@GetMapping("/owners/{ownerId}")
|
||||||
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
|
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
|
||||||
|
DependencyLogger.log("Displaying owner details for ownerId: " + ownerId);
|
||||||
ModelAndView mav = new ModelAndView("owners/ownerDetails");
|
ModelAndView mav = new ModelAndView("owners/ownerDetails");
|
||||||
Optional<Owner> optionalOwner = this.owners.findById(ownerId);
|
Optional<Owner> optionalOwner = this.owners.findById(ownerId);
|
||||||
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
||||||
|
|
|
@ -19,11 +19,13 @@ import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository class for <code>Owner</code> domain objects All method names are compliant
|
* Repository class for <code>Owner</code> domain objects All method names are compliant
|
||||||
|
@ -53,7 +55,7 @@ public interface OwnerRepository extends JpaRepository<Owner, Integer> {
|
||||||
* @return a Collection of matching {@link Owner}s (or an empty Collection if none
|
* @return a Collection of matching {@link Owner}s (or an empty Collection if none
|
||||||
* found)
|
* found)
|
||||||
*/
|
*/
|
||||||
Page<Owner> findByLastNameStartingWith(String lastName, Pageable pageable);
|
Page<Owner> findByLastNameStartingWith(String lastName, Pageable pageable, String city);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve an {@link Owner} from the data store by id.
|
* Retrieve an {@link Owner} from the data store by id.
|
||||||
|
@ -75,4 +77,16 @@ public interface OwnerRepository extends JpaRepository<Owner, Integer> {
|
||||||
**/
|
**/
|
||||||
Page<Owner> findAll(Pageable pageable);
|
Page<Owner> findAll(Pageable pageable);
|
||||||
|
|
||||||
|
Page<Owner> findByLastNameStartingWith(String lastName, Pageable pageable);
|
||||||
|
|
||||||
|
@Query("SELECT o FROM Owner o WHERE o.lastName LIKE :lastName% AND (:city IS NULL OR o.city = :city)")
|
||||||
|
Page<Owner> findByLastNameAndCity(@Param("lastName") String lastName, @Param("city") String city,
|
||||||
|
Pageable pageable);
|
||||||
|
|
||||||
|
List<Owner> findByLastName(String lastName);
|
||||||
|
|
||||||
|
Page<Owner> findByCity(@NotBlank String city, Pageable pageable);
|
||||||
|
|
||||||
|
List<Owner> city(@NotBlank String city);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.samples.petclinic.util.DependencyLogger;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class OwnerService {
|
||||||
|
|
||||||
|
private final OwnerRepository ownerRepository;
|
||||||
|
|
||||||
|
public OwnerService(OwnerRepository ownerRepository) {
|
||||||
|
this.ownerRepository = ownerRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<Owner> findOwners(String lastName, String city, int page) {
|
||||||
|
Pageable pageable = PageRequest.of(page - 1, 5);
|
||||||
|
|
||||||
|
if (city == null || city.isEmpty()) {
|
||||||
|
DependencyLogger.log("Searching owners by lastName only: " + lastName);
|
||||||
|
return ownerRepository.findByLastNameStartingWith(lastName, pageable);
|
||||||
|
}
|
||||||
|
DependencyLogger.log("Searching owners by lastName and city: " + lastName + ", " + city);
|
||||||
|
return ownerRepository.findByLastNameAndCity(lastName, city, pageable);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class OwnerServiceTest {
|
||||||
|
|
||||||
|
private OwnerRepository ownerRepository; // Mocked repository
|
||||||
|
|
||||||
|
private OwnerService ownerService; // Service to be tested
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
// Mocking the repository
|
||||||
|
ownerRepository = mock(OwnerRepository.class);
|
||||||
|
// Injecting the mock into the service
|
||||||
|
ownerService = new OwnerService(ownerRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFindOwnersByLastNameOnly() {
|
||||||
|
// Arrange: Prepare mock return value
|
||||||
|
Pageable pageable = PageRequest.of(0, 5);
|
||||||
|
Owner owner = new Owner();
|
||||||
|
owner.setLastName("Smith");
|
||||||
|
Page<Owner> ownersPage = new PageImpl<>(Collections.singletonList(owner));
|
||||||
|
when(ownerRepository.findByLastNameStartingWith(eq("Smith"), any(Pageable.class))).thenReturn(ownersPage);
|
||||||
|
|
||||||
|
// Act: Call the service
|
||||||
|
Page<Owner> result = ownerService.findOwners("Smith", null, 1);
|
||||||
|
|
||||||
|
// Assert: Verify results
|
||||||
|
assertEquals(1, result.getTotalElements()); // Only 1 result is expected
|
||||||
|
assertEquals("Smith", result.getContent().get(0).getLastName()); // Verify last
|
||||||
|
// name
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFindOwnersByLastNameAndCity() {
|
||||||
|
// Arrange: Prepare mock return value
|
||||||
|
Pageable pageable = PageRequest.of(0, 5);
|
||||||
|
Owner owner = new Owner();
|
||||||
|
owner.setLastName("Smith");
|
||||||
|
owner.setCity("New York");
|
||||||
|
Page<Owner> ownersPage = new PageImpl<>(Collections.singletonList(owner));
|
||||||
|
when(ownerRepository.findByLastNameAndCity(eq("Smith"), eq("New York"), any(Pageable.class)))
|
||||||
|
.thenReturn(ownersPage);
|
||||||
|
|
||||||
|
// Act: Call the service
|
||||||
|
Page<Owner> result = ownerService.findOwners("Smith", "New York", 1);
|
||||||
|
|
||||||
|
// Assert: Verify results
|
||||||
|
assertEquals(1, result.getTotalElements()); // Only 1 result is expected
|
||||||
|
assertEquals("Smith", result.getContent().get(0).getLastName()); // Verify last
|
||||||
|
// name
|
||||||
|
assertEquals("New York", result.getContent().get(0).getCity()); // Verify city
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFindOwnersNoResults() {
|
||||||
|
// Arrange: Mock repository to return an empty page
|
||||||
|
Pageable pageable = PageRequest.of(0, 5);
|
||||||
|
Page<Owner> emptyPage = new PageImpl<>(Collections.emptyList());
|
||||||
|
when(ownerRepository.findByLastNameStartingWith(eq("Unknown"), any(Pageable.class))).thenReturn(emptyPage);
|
||||||
|
|
||||||
|
// Act: Call the service
|
||||||
|
Page<Owner> result = ownerService.findOwners("Unknown", null, 1);
|
||||||
|
|
||||||
|
// Assert: Verify results
|
||||||
|
assertTrue(result.isEmpty()); // No results expected
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.validation.Validator;
|
||||||
|
|
||||||
|
public class OwnerValidator implements Validator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> clazz) {
|
||||||
|
return Owner.class.isAssignableFrom(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(Object target, Errors errors) {
|
||||||
|
Owner owner = (Owner) target;
|
||||||
|
|
||||||
|
// Ensure lastName is not empty
|
||||||
|
if (owner.getTelephone() == null || owner.getTelephone().length() != 10) {
|
||||||
|
errors.rejectValue("telephone", "required", "Valid telephone number is required.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.springframework.samples.petclinic.util;
|
||||||
|
|
||||||
|
import static java.lang.System.*;
|
||||||
|
|
||||||
|
public class DependencyLogger {
|
||||||
|
|
||||||
|
public static void log(String message) {
|
||||||
|
System.out.println("Trace Log: " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -207,7 +207,8 @@ body {
|
||||||
font-weight: var(--bs-body-font-weight);
|
font-weight: var(--bs-body-font-weight);
|
||||||
line-height: var(--bs-body-line-height);
|
line-height: var(--bs-body-line-height);
|
||||||
color: var(--bs-body-color);
|
color: var(--bs-body-color);
|
||||||
text-align: var(--bs-body-text-align);
|
/*noinspection CssUnresolvedCustomProperty*/
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
background-color: var(--bs-body-bg);
|
background-color: var(--bs-body-bg);
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
|
||||||
|
@ -674,7 +675,7 @@ progress {
|
||||||
margin-top: var(--bs-gutter-y); }
|
margin-top: var(--bs-gutter-y); }
|
||||||
|
|
||||||
.col {
|
.col {
|
||||||
flex: 1 0 0%; }
|
flex: 1 0 0; }
|
||||||
|
|
||||||
.row-cols-auto > * {
|
.row-cols-auto > * {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
@ -839,7 +840,7 @@ progress {
|
||||||
|
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
.col-sm {
|
.col-sm {
|
||||||
flex: 1 0 0%; }
|
flex: 1 0 0; }
|
||||||
.row-cols-sm-auto > * {
|
.row-cols-sm-auto > * {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: auto; }
|
width: auto; }
|
||||||
|
@ -963,7 +964,7 @@ progress {
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.col-md {
|
.col-md {
|
||||||
flex: 1 0 0%; }
|
flex: 1 0 0; }
|
||||||
.row-cols-md-auto > * {
|
.row-cols-md-auto > * {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: auto; }
|
width: auto; }
|
||||||
|
@ -1087,7 +1088,7 @@ progress {
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
.col-lg {
|
.col-lg {
|
||||||
flex: 1 0 0%; }
|
flex: 1 0 0; }
|
||||||
.row-cols-lg-auto > * {
|
.row-cols-lg-auto > * {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: auto; }
|
width: auto; }
|
||||||
|
@ -1211,7 +1212,7 @@ progress {
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
.col-xl {
|
.col-xl {
|
||||||
flex: 1 0 0%; }
|
flex: 1 0 0; }
|
||||||
.row-cols-xl-auto > * {
|
.row-cols-xl-auto > * {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: auto; }
|
width: auto; }
|
||||||
|
@ -1335,7 +1336,7 @@ progress {
|
||||||
|
|
||||||
@media (min-width: 1400px) {
|
@media (min-width: 1400px) {
|
||||||
.col-xxl {
|
.col-xxl {
|
||||||
flex: 1 0 0%; }
|
flex: 1 0 0; }
|
||||||
.row-cols-xxl-auto > * {
|
.row-cols-xxl-auto > * {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: auto; }
|
width: auto; }
|
||||||
|
@ -1712,7 +1713,8 @@ progress {
|
||||||
min-width: 85px;
|
min-width: 85px;
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
margin: 0; }
|
margin: 0; }
|
||||||
.form-control::-webkit-datetime-edit {
|
.form-control::-webkit-inner-spin-button,
|
||||||
|
.form-control::-webkit-calendar-picker-indicator {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0; }
|
padding: 0; }
|
||||||
.form-control::placeholder {
|
.form-control::placeholder {
|
||||||
|
@ -2930,7 +2932,8 @@ textarea.form-control-lg {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: var(--bs-dropdown-item-border-radius, 0); }
|
/*noinspection CssUnresolvedCustomProperty*/
|
||||||
|
border-radius: var(--bs-dropdown-item-border-radius, 0); }
|
||||||
.dropdown-item:hover, .dropdown-item:focus {
|
.dropdown-item:hover, .dropdown-item:focus {
|
||||||
color: var(--bs-dropdown-link-hover-color);
|
color: var(--bs-dropdown-link-hover-color);
|
||||||
background-color: var(--bs-dropdown-link-hover-bg); }
|
background-color: var(--bs-dropdown-link-hover-bg); }
|
||||||
|
@ -3070,7 +3073,8 @@ textarea.form-control-lg {
|
||||||
.nav-link {
|
.nav-link {
|
||||||
display: block;
|
display: block;
|
||||||
padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);
|
padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);
|
||||||
font-size: var(--bs-nav-link-font-size);
|
/*noinspection CssUnresolvedCustomProperty*/
|
||||||
|
font-size: var(--bs-nav-link-font-size);
|
||||||
font-weight: var(--bs-nav-link-font-weight);
|
font-weight: var(--bs-nav-link-font-weight);
|
||||||
color: var(--bs-nav-link-color);
|
color: var(--bs-nav-link-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -3277,7 +3281,8 @@ textarea.form-control-lg {
|
||||||
background-size: 100%; }
|
background-size: 100%; }
|
||||||
|
|
||||||
.navbar-nav-scroll {
|
.navbar-nav-scroll {
|
||||||
max-height: var(--bs-scroll-height, 75vh);
|
/*noinspection CssUnresolvedCustomProperty*/
|
||||||
|
max-height: var(--bs-scroll-height, 75vh);
|
||||||
overflow-y: auto; }
|
overflow-y: auto; }
|
||||||
|
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
|
@ -3646,7 +3651,7 @@ textarea.form-control-lg {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap; }
|
flex-flow: row wrap; }
|
||||||
.card-group > .card {
|
.card-group > .card {
|
||||||
flex: 1 0 0%;
|
flex: 1 0 0;
|
||||||
margin-bottom: 0; }
|
margin-bottom: 0; }
|
||||||
.card-group > .card + .card {
|
.card-group > .card + .card {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
@ -3795,7 +3800,8 @@ textarea.form-control-lg {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);
|
padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);
|
||||||
margin-bottom: var(--bs-breadcrumb-margin-bottom);
|
margin-bottom: var(--bs-breadcrumb-margin-bottom);
|
||||||
font-size: var(--bs-breadcrumb-font-size);
|
/*noinspection CssUnresolvedCustomProperty*/
|
||||||
|
font-size: var(--bs-breadcrumb-font-size);
|
||||||
list-style: none;
|
list-style: none;
|
||||||
background-color: var(--bs-breadcrumb-bg);
|
background-color: var(--bs-breadcrumb-bg);
|
||||||
border-radius: var(--bs-breadcrumb-border-radius); }
|
border-radius: var(--bs-breadcrumb-border-radius); }
|
||||||
|
@ -3806,7 +3812,8 @@ textarea.form-control-lg {
|
||||||
float: left;
|
float: left;
|
||||||
padding-right: var(--bs-breadcrumb-item-padding-x);
|
padding-right: var(--bs-breadcrumb-item-padding-x);
|
||||||
color: var(--bs-breadcrumb-divider-color);
|
color: var(--bs-breadcrumb-divider-color);
|
||||||
content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; }
|
/*noinspection CssUnresolvedCustomProperty*/
|
||||||
|
content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; }
|
||||||
|
|
||||||
.breadcrumb-item.active {
|
.breadcrumb-item.active {
|
||||||
color: var(--bs-breadcrumb-item-active-color); }
|
color: var(--bs-breadcrumb-item-active-color); }
|
||||||
|
@ -5566,7 +5573,7 @@ textarea.form-control-lg {
|
||||||
|
|
||||||
@keyframes placeholder-wave {
|
@keyframes placeholder-wave {
|
||||||
100% {
|
100% {
|
||||||
mask-position: -200% 0%; } }
|
mask-position: -200% 0; } }
|
||||||
|
|
||||||
.clearfix::after {
|
.clearfix::after {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -5670,7 +5677,8 @@ textarea.form-control-lg {
|
||||||
|
|
||||||
.focus-ring:focus {
|
.focus-ring:focus {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); }
|
/*noinspection CssUnresolvedCustomProperty*/
|
||||||
|
box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); }
|
||||||
|
|
||||||
.icon-link {
|
.icon-link {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -5689,7 +5697,8 @@ textarea.form-control-lg {
|
||||||
.icon-link > .bi {
|
.icon-link > .bi {
|
||||||
transition: none; } }
|
transition: none; } }
|
||||||
.icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi {
|
.icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi {
|
||||||
transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); }
|
/*noinspection CssUnresolvedCustomProperty*/
|
||||||
|
transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); }
|
||||||
|
|
||||||
.ratio {
|
.ratio {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -9358,7 +9367,7 @@ table td.action-column {
|
||||||
display: block; }
|
display: block; }
|
||||||
|
|
||||||
.cluster-view .input-group-addon {
|
.cluster-view .input-group-addon {
|
||||||
width: 0%; }
|
width: 0; }
|
||||||
|
|
||||||
.cluster-view {
|
.cluster-view {
|
||||||
margin-bottom: 0; }
|
margin-bottom: 0; }
|
||||||
|
@ -9432,7 +9441,7 @@ strong {
|
||||||
.navbar {
|
.navbar {
|
||||||
border-top: 4px solid #6db33f;
|
border-top: 4px solid #6db33f;
|
||||||
background-color: #34302d;
|
background-color: #34302d;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
border-right: 0; }
|
border-right: 0; }
|
||||||
|
@ -9494,8 +9503,8 @@ strong {
|
||||||
.navbar-toggle {
|
.navbar-toggle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
left: 0px;
|
left: 0;
|
||||||
top: 0px; }
|
top: 0; }
|
||||||
.navbar a.navbar-brand {
|
.navbar a.navbar-brand {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto 0 auto;
|
margin: 0 auto 0 auto;
|
||||||
|
@ -9517,4 +9526,4 @@ strong {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 30px; } }
|
margin-bottom: 30px; } }
|
||||||
|
|
||||||
/*# sourceMappingURL=../../../../../../target/petclinic.css.map */
|
/*# sourceMappingURL=../../../../../../target/petclinic.css.map */
|
||||||
|
|
|
@ -1,34 +1,51 @@
|
||||||
<html xmlns:th="https://www.thymeleaf.org"
|
<html xmlns:th="https://www.thymeleaf.org"
|
||||||
th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
|
th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h2>Find Owners</h2>
|
<h2>Find Owners</h2>
|
||||||
|
|
||||||
<form th:object="${owner}" th:action="@{/owners}" method="get"
|
<form th:object="${owner}" th:action="@{/owners}" method="get"
|
||||||
class="form-horizontal" id="search-owner-form">
|
class="form-horizontal" id="search-owner-form">
|
||||||
<div class="form-group">
|
|
||||||
<div class="control-group" id="lastNameGroup">
|
<!-- Last Name Field -->
|
||||||
<label class="col-sm-2 control-label">Last name </label>
|
<div class="form-group">
|
||||||
<div class="col-sm-10">
|
<div class="control-group" id="lastNameGroup">
|
||||||
<input class="form-control" th:field="*{lastName}" size="30"
|
<label class="col-sm-2 control-label">Last name </label>
|
||||||
maxlength="80" /> <span class="help-inline"><div
|
<div class="col-sm-10">
|
||||||
th:if="${#fields.hasAnyErrors()}">
|
<input class="form-control" th:field="*{lastName}" size="30"
|
||||||
|
maxlength="80" />
|
||||||
|
<span class="help-inline">
|
||||||
|
<div th:if="${#fields.hasAnyErrors()}">
|
||||||
<p th:each="err : ${#fields.allErrors()}" th:text="${err}">Error</p>
|
<p th:each="err : ${#fields.allErrors()}" th:text="${err}">Error</p>
|
||||||
</div></span>
|
</div>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
|
||||||
<button type="submit" class="btn btn-primary">Find
|
<!-- City Field -->
|
||||||
Owner</button>
|
<div class="form-group">
|
||||||
|
<div class="control-group" id="cityGroup">
|
||||||
|
<label class="col-sm-2 control-label">City </label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" name="city" placeholder="City" size="30"
|
||||||
|
maxlength="80" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a class="btn btn-primary" th:href="@{/owners/new}">Add Owner</a>
|
<!-- Submit Button -->
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<button type="submit" class="btn btn-primary">Find Owner</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
<!-- Add Owner Button -->
|
||||||
|
<a class="btn btn-primary" th:href="@{/owners/new}">Add Owner</a>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
0
src/main/scss/_bootstrap.scss
Normal file
0
src/main/scss/_bootstrap.scss
Normal file
|
@ -1,14 +1,14 @@
|
||||||
.navbar {
|
.navbar {
|
||||||
border-top: 4px solid #6db33f;
|
border-top: 4px solid #6db33f;
|
||||||
background-color: #34302d;
|
background-color: #34302d;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar a.navbar-brand {
|
.navbar a.navbar-brand {
|
||||||
background: url("../images/spring-logo-dataflow.png") -1px -1px no-repeat;
|
background: url("../correct/path/to/spring-logo-dataflow.png") -1px -1px no-repeat;
|
||||||
margin: 12px 0 6px;
|
margin: 12px 0 6px;
|
||||||
width: 229px;
|
width: 229px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
display: block;
|
display: block;
|
||||||
width: 229px;
|
width: 229px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
background: url("../images/spring-logo-dataflow.png") -1px -48px no-repeat;
|
background: url("/correct/path/to/spring-logo-dataflow.png") -1px -48px no-repeat;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-moz-transition: opacity 0.12s ease-in-out;
|
-moz-transition: opacity 0.12s ease-in-out;
|
||||||
-webkit-transition: opacity 0.12s ease-in-out;
|
-webkit-transition: opacity 0.12s ease-in-out;
|
||||||
|
|
|
@ -172,7 +172,7 @@ table td.action-column {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cluster-view .input-group-addon {
|
.cluster-view .input-group-addon {
|
||||||
width: 0%;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cluster-view {
|
.cluster-view {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
.navbar-toggle {
|
.navbar-toggle {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
left:0px;
|
left:0;
|
||||||
top:0px;
|
top:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar a.navbar-brand {
|
.navbar a.navbar-brand {
|
||||||
|
|
|
@ -30,6 +30,8 @@ import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
public class PetClinicIntegrationTests {
|
public class PetClinicIntegrationTests {
|
||||||
|
|
|
@ -16,11 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.samples.petclinic.model;
|
package org.springframework.samples.petclinic.model;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
|
|
Loading…
Reference in a new issue