mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-20 06:45: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"?>
|
||||
<!--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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -32,11 +33,13 @@
|
|||
|
||||
<checkstyle.version>10.20.1</checkstyle.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>
|
||||
<maven-checkstyle.version>3.6.0</maven-checkstyle.version>
|
||||
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.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>
|
||||
|
||||
|
@ -146,11 +149,39 @@
|
|||
<artifactId>mysql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>4.13.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
</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>
|
||||
|
||||
|
@ -196,10 +227,27 @@
|
|||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>${maven-checkstyle.version}</version>
|
||||
<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>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>${checkstyle.version}</version>
|
||||
<version>10.12.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.spring.nohttp</groupId>
|
||||
|
@ -209,7 +257,7 @@
|
|||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>nohttp-checkstyle-validation</id>
|
||||
<id>validation-checkstyle</id>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
|
@ -297,6 +345,18 @@
|
|||
</licenses>
|
||||
|
||||
<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>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
|
@ -341,6 +401,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
|
@ -366,7 +427,8 @@
|
|||
<plugin>
|
||||
<groupId>com.gitlab.haynes</groupId>
|
||||
<artifactId>libsass-maven-plugin</artifactId>
|
||||
<version>${libsass.version}</version>
|
||||
<version>0.7.0</version>
|
||||
|
||||
<configuration>
|
||||
<inputPath>${basedir}/src/main/scss/</inputPath>
|
||||
<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;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -24,6 +9,7 @@ import org.springframework.data.domain.Pageable;
|
|||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.samples.petclinic.util.DependencyLogger;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Ken Krebs
|
||||
* @author Arjen Poutsma
|
||||
* @author Michael Isvy
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@Controller
|
||||
class OwnerController {
|
||||
|
||||
|
@ -50,17 +30,33 @@ class OwnerController {
|
|||
|
||||
private final OwnerRepository owners;
|
||||
|
||||
public OwnerController(OwnerRepository owners) {
|
||||
private final OwnerService ownerService;
|
||||
|
||||
public OwnerController(OwnerRepository owners, OwnerService ownerService) {
|
||||
this.owners = owners;
|
||||
this.ownerService = ownerService;
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
|
||||
private void addOwnerValidator(WebDataBinder dataBinder) {
|
||||
DependencyLogger.log("Adding OwnerValidator to WebDataBinder");
|
||||
dataBinder.addValidators((Validator) new OwnerValidator());
|
||||
}
|
||||
|
||||
@ModelAttribute("owner")
|
||||
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
||||
DependencyLogger.log("findOwner called with ownerId: " + ownerId);
|
||||
return ownerId == null ? new Owner()
|
||||
: this.owners.findById(ownerId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId
|
||||
|
@ -69,53 +65,60 @@ class OwnerController {
|
|||
|
||||
@GetMapping("/owners/new")
|
||||
public String initCreationForm() {
|
||||
DependencyLogger.log("Initializing creation form for new owner");
|
||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
@PostMapping("/owners/new")
|
||||
public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) {
|
||||
DependencyLogger.log("Processing creation form for new owner");
|
||||
if (result.hasErrors()) {
|
||||
DependencyLogger.log("Validation errors occurred while creating owner");
|
||||
redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner.");
|
||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
this.owners.save(owner);
|
||||
DependencyLogger.log("New owner saved with ID: " + owner.getId());
|
||||
redirectAttributes.addFlashAttribute("message", "New Owner Created");
|
||||
return "redirect:/owners/" + owner.getId();
|
||||
}
|
||||
|
||||
@GetMapping("/owners/find")
|
||||
public String initFindForm() {
|
||||
DependencyLogger.log("Initializing find form for owners");
|
||||
return "owners/findOwners";
|
||||
}
|
||||
|
||||
@GetMapping("/owners")
|
||||
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
|
||||
Model model) {
|
||||
// allow parameterless GET request for /owners to return all records
|
||||
public String processFindForm(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(required = false) String city, Owner owner, BindingResult result, Model model) {
|
||||
DependencyLogger.log("Processing find form for owners, page: " + page + ", city: " + city);
|
||||
|
||||
if (owner.getLastName() == null) {
|
||||
owner.setLastName(""); // empty string signifies broadest possible search
|
||||
owner.setLastName(""); // Broadest possible search
|
||||
}
|
||||
|
||||
// find owners by last name
|
||||
Page<Owner> ownersResults = findPaginatedForOwnersLastName(page, owner.getLastName());
|
||||
// Use ownerService to fetch paginated results
|
||||
Page<Owner> ownersResults = ownerService.findOwners(owner.getLastName(), city, page);
|
||||
|
||||
if (ownersResults.isEmpty()) {
|
||||
// no owners found
|
||||
DependencyLogger.log("No owners found for last name: " + owner.getLastName());
|
||||
result.rejectValue("lastName", "notFound", "not found");
|
||||
return "owners/findOwners";
|
||||
}
|
||||
|
||||
if (ownersResults.getTotalElements() == 1) {
|
||||
// 1 owner found
|
||||
owner = ownersResults.iterator().next();
|
||||
return "redirect:/owners/" + owner.getId();
|
||||
}
|
||||
|
||||
// multiple owners found
|
||||
DependencyLogger.log("Multiple owners found, adding pagination model.");
|
||||
return addPaginationModel(page, model, ownersResults);
|
||||
}
|
||||
|
||||
private String addPaginationModel(int page, Model model, Page<Owner> paginated) {
|
||||
DependencyLogger.log("Adding pagination model for page: " + page);
|
||||
List<Owner> listOwners = paginated.getContent();
|
||||
model.addAttribute("currentPage", page);
|
||||
model.addAttribute("totalPages", paginated.getTotalPages());
|
||||
|
@ -125,25 +128,31 @@ class OwnerController {
|
|||
}
|
||||
|
||||
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
||||
DependencyLogger.log("Finding paginated owners for last name: " + lastname + ", page: " + page);
|
||||
int pageSize = 5;
|
||||
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
||||
return owners.findByLastNameStartingWith(lastname, pageable);
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/owners/{ownerId}/edit")
|
||||
public String initUpdateOwnerForm() {
|
||||
DependencyLogger.log("Initializing update form for owner");
|
||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
@PostMapping("/owners/{ownerId}/edit")
|
||||
public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
DependencyLogger.log("Processing update form for owner with ID: " + ownerId);
|
||||
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.");
|
||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
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.");
|
||||
redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again.");
|
||||
return "redirect:/owners/{ownerId}/edit";
|
||||
|
@ -151,17 +160,14 @@ class OwnerController {
|
|||
|
||||
owner.setId(ownerId);
|
||||
this.owners.save(owner);
|
||||
DependencyLogger.log("Owner updated with ID: " + ownerId);
|
||||
redirectAttributes.addFlashAttribute("message", "Owner Values Updated");
|
||||
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}")
|
||||
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
|
||||
DependencyLogger.log("Displaying owner details for ownerId: " + ownerId);
|
||||
ModelAndView mav = new ModelAndView("owners/ownerDetails");
|
||||
Optional<Owner> optionalOwner = this.owners.findById(ownerId);
|
||||
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
||||
|
|
|
@ -19,11 +19,13 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
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.transaction.annotation.Transactional;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
|
@ -75,4 +77,16 @@ public interface OwnerRepository extends JpaRepository<Owner, Integer> {
|
|||
**/
|
||||
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,6 +207,7 @@ body {
|
|||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
|
@ -674,7 +675,7 @@ progress {
|
|||
margin-top: var(--bs-gutter-y); }
|
||||
|
||||
.col {
|
||||
flex: 1 0 0%; }
|
||||
flex: 1 0 0; }
|
||||
|
||||
.row-cols-auto > * {
|
||||
flex: 0 0 auto;
|
||||
|
@ -839,7 +840,7 @@ progress {
|
|||
|
||||
@media (min-width: 576px) {
|
||||
.col-sm {
|
||||
flex: 1 0 0%; }
|
||||
flex: 1 0 0; }
|
||||
.row-cols-sm-auto > * {
|
||||
flex: 0 0 auto;
|
||||
width: auto; }
|
||||
|
@ -963,7 +964,7 @@ progress {
|
|||
|
||||
@media (min-width: 768px) {
|
||||
.col-md {
|
||||
flex: 1 0 0%; }
|
||||
flex: 1 0 0; }
|
||||
.row-cols-md-auto > * {
|
||||
flex: 0 0 auto;
|
||||
width: auto; }
|
||||
|
@ -1087,7 +1088,7 @@ progress {
|
|||
|
||||
@media (min-width: 992px) {
|
||||
.col-lg {
|
||||
flex: 1 0 0%; }
|
||||
flex: 1 0 0; }
|
||||
.row-cols-lg-auto > * {
|
||||
flex: 0 0 auto;
|
||||
width: auto; }
|
||||
|
@ -1211,7 +1212,7 @@ progress {
|
|||
|
||||
@media (min-width: 1200px) {
|
||||
.col-xl {
|
||||
flex: 1 0 0%; }
|
||||
flex: 1 0 0; }
|
||||
.row-cols-xl-auto > * {
|
||||
flex: 0 0 auto;
|
||||
width: auto; }
|
||||
|
@ -1335,7 +1336,7 @@ progress {
|
|||
|
||||
@media (min-width: 1400px) {
|
||||
.col-xxl {
|
||||
flex: 1 0 0%; }
|
||||
flex: 1 0 0; }
|
||||
.row-cols-xxl-auto > * {
|
||||
flex: 0 0 auto;
|
||||
width: auto; }
|
||||
|
@ -1712,7 +1713,8 @@ progress {
|
|||
min-width: 85px;
|
||||
height: 1.5em;
|
||||
margin: 0; }
|
||||
.form-control::-webkit-datetime-edit {
|
||||
.form-control::-webkit-inner-spin-button,
|
||||
.form-control::-webkit-calendar-picker-indicator {
|
||||
display: block;
|
||||
padding: 0; }
|
||||
.form-control::placeholder {
|
||||
|
@ -2930,6 +2932,7 @@ textarea.form-control-lg {
|
|||
white-space: nowrap;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
border-radius: var(--bs-dropdown-item-border-radius, 0); }
|
||||
.dropdown-item:hover, .dropdown-item:focus {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
|
@ -3070,6 +3073,7 @@ textarea.form-control-lg {
|
|||
.nav-link {
|
||||
display: block;
|
||||
padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
font-size: var(--bs-nav-link-font-size);
|
||||
font-weight: var(--bs-nav-link-font-weight);
|
||||
color: var(--bs-nav-link-color);
|
||||
|
@ -3277,6 +3281,7 @@ textarea.form-control-lg {
|
|||
background-size: 100%; }
|
||||
|
||||
.navbar-nav-scroll {
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
max-height: var(--bs-scroll-height, 75vh);
|
||||
overflow-y: auto; }
|
||||
|
||||
|
@ -3646,7 +3651,7 @@ textarea.form-control-lg {
|
|||
display: flex;
|
||||
flex-flow: row wrap; }
|
||||
.card-group > .card {
|
||||
flex: 1 0 0%;
|
||||
flex: 1 0 0;
|
||||
margin-bottom: 0; }
|
||||
.card-group > .card + .card {
|
||||
margin-left: 0;
|
||||
|
@ -3795,6 +3800,7 @@ textarea.form-control-lg {
|
|||
flex-wrap: wrap;
|
||||
padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);
|
||||
margin-bottom: var(--bs-breadcrumb-margin-bottom);
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
font-size: var(--bs-breadcrumb-font-size);
|
||||
list-style: none;
|
||||
background-color: var(--bs-breadcrumb-bg);
|
||||
|
@ -3806,6 +3812,7 @@ textarea.form-control-lg {
|
|||
float: left;
|
||||
padding-right: var(--bs-breadcrumb-item-padding-x);
|
||||
color: var(--bs-breadcrumb-divider-color);
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; }
|
||||
|
||||
.breadcrumb-item.active {
|
||||
|
@ -5566,7 +5573,7 @@ textarea.form-control-lg {
|
|||
|
||||
@keyframes placeholder-wave {
|
||||
100% {
|
||||
mask-position: -200% 0%; } }
|
||||
mask-position: -200% 0; } }
|
||||
|
||||
.clearfix::after {
|
||||
display: block;
|
||||
|
@ -5670,6 +5677,7 @@ textarea.form-control-lg {
|
|||
|
||||
.focus-ring:focus {
|
||||
outline: 0;
|
||||
/*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 {
|
||||
|
@ -5689,6 +5697,7 @@ textarea.form-control-lg {
|
|||
.icon-link > .bi {
|
||||
transition: none; } }
|
||||
.icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi {
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); }
|
||||
|
||||
.ratio {
|
||||
|
@ -9358,7 +9367,7 @@ table td.action-column {
|
|||
display: block; }
|
||||
|
||||
.cluster-view .input-group-addon {
|
||||
width: 0%; }
|
||||
width: 0; }
|
||||
|
||||
.cluster-view {
|
||||
margin-bottom: 0; }
|
||||
|
@ -9432,7 +9441,7 @@ strong {
|
|||
.navbar {
|
||||
border-top: 4px solid #6db33f;
|
||||
background-color: #34302d;
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 0;
|
||||
border-left: 0;
|
||||
border-right: 0; }
|
||||
|
@ -9494,8 +9503,8 @@ strong {
|
|||
.navbar-toggle {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
left: 0px;
|
||||
top: 0px; }
|
||||
left: 0;
|
||||
top: 0; }
|
||||
.navbar a.navbar-brand {
|
||||
display: block;
|
||||
margin: 0 auto 0 auto;
|
||||
|
|
|
@ -7,25 +7,42 @@
|
|||
|
||||
<form th:object="${owner}" th:action="@{/owners}" method="get"
|
||||
class="form-horizontal" id="search-owner-form">
|
||||
|
||||
<!-- Last Name Field -->
|
||||
<div class="form-group">
|
||||
<div class="control-group" id="lastNameGroup">
|
||||
<label class="col-sm-2 control-label">Last name </label>
|
||||
<div class="col-sm-10">
|
||||
<input class="form-control" th:field="*{lastName}" size="30"
|
||||
maxlength="80" /> <span class="help-inline"><div
|
||||
th:if="${#fields.hasAnyErrors()}">
|
||||
maxlength="80" />
|
||||
<span class="help-inline">
|
||||
<div th:if="${#fields.hasAnyErrors()}">
|
||||
<p th:each="err : ${#fields.allErrors()}" th:text="${err}">Error</p>
|
||||
</div></span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<!-- City Field -->
|
||||
<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>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Add Owner Button -->
|
||||
<a class="btn btn-primary" th:href="@{/owners/new}">Add Owner</a>
|
||||
|
||||
</form>
|
||||
|
|
0
src/main/scss/_bootstrap.scss
Normal file
0
src/main/scss/_bootstrap.scss
Normal file
|
@ -1,14 +1,14 @@
|
|||
.navbar {
|
||||
border-top: 4px solid #6db33f;
|
||||
background-color: #34302d;
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.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;
|
||||
width: 229px;
|
||||
height: 46px;
|
||||
|
@ -21,7 +21,7 @@
|
|||
display: block;
|
||||
width: 229px;
|
||||
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;
|
||||
-moz-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 {
|
||||
width: 0%;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.cluster-view {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
.navbar-toggle {
|
||||
position:absolute;
|
||||
z-index: 9999;
|
||||
left:0px;
|
||||
top:0px;
|
||||
left:0;
|
||||
top:0;
|
||||
}
|
||||
|
||||
.navbar a.navbar-brand {
|
||||
|
|
|
@ -30,6 +30,8 @@ import org.springframework.http.RequestEntity;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||
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)
|
||||
public class PetClinicIntegrationTests {
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
|
||||
package org.springframework.samples.petclinic.model;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
|
Loading…
Reference in a new issue