Compare commits
3 Commits
1e4d20b6a8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d22b7ddcaf | |||
| bdcbdb850a | |||
| 232422d9d9 |
@@ -0,0 +1,202 @@
|
|||||||
|
/target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/git,java,maven,eclipse,windows
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
### Eclipse Patch ###
|
||||||
|
# Eclipse Core
|
||||||
|
.project
|
||||||
|
|
||||||
|
# JDT-specific (Eclipse Java Development Tools)
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated
|
||||||
|
|
||||||
|
.sts4-cache/
|
||||||
|
|
||||||
|
### Git ###
|
||||||
|
# Created by git for backups. To disable backups in Git:
|
||||||
|
# $ git config --global mergetool.keepBackup false
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# Created by git when using merge tools for conflicts
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
*.REMOTE.*
|
||||||
|
*_BACKUP_*.txt
|
||||||
|
*_BASE_*.txt
|
||||||
|
*_LOCAL_*.txt
|
||||||
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### Maven ###
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Some additional ignores (sort later)
|
||||||
|
*.DS_Store
|
||||||
|
*.sw?
|
||||||
|
.#*
|
||||||
|
*#
|
||||||
|
*~
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
bin
|
||||||
|
build
|
||||||
|
target
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
*.sublime-*
|
||||||
|
/scratch
|
||||||
|
.gradle
|
||||||
|
README.html
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
.exercism
|
||||||
|
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
/target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/git,java,maven,eclipse,windows
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
### Eclipse Patch ###
|
||||||
|
# Eclipse Core
|
||||||
|
.project
|
||||||
|
|
||||||
|
# JDT-specific (Eclipse Java Development Tools)
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated
|
||||||
|
|
||||||
|
.sts4-cache/
|
||||||
|
|
||||||
|
### Git ###
|
||||||
|
# Created by git for backups. To disable backups in Git:
|
||||||
|
# $ git config --global mergetool.keepBackup false
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# Created by git when using merge tools for conflicts
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
*.REMOTE.*
|
||||||
|
*_BACKUP_*.txt
|
||||||
|
*_BASE_*.txt
|
||||||
|
*_LOCAL_*.txt
|
||||||
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### Maven ###
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Some additional ignores (sort later)
|
||||||
|
*.DS_Store
|
||||||
|
*.sw?
|
||||||
|
.#*
|
||||||
|
*#
|
||||||
|
*~
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
bin
|
||||||
|
build
|
||||||
|
target
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
*.sublime-*
|
||||||
|
/scratch
|
||||||
|
.gradle
|
||||||
|
README.html
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
.exercism
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package ua.nure.jfn.task2;
|
package ua.nure.jfn.task2;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
public class ArrayImpl<T> implements Array<T> {
|
public class ArrayImpl<T> implements Array<T> {
|
||||||
|
|
||||||
@@ -11,10 +12,10 @@ public class ArrayImpl<T> implements Array<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ArrayImpl(Container<T> container) {
|
public ArrayImpl(Container<T> container) {
|
||||||
this.elements = new Object[container.size() + 1];
|
elements = new Object[container.size() + 1];
|
||||||
|
|
||||||
for (T element : container)
|
for (T element : container)
|
||||||
this.add(element);
|
add(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,7 +44,7 @@ public class ArrayImpl<T> implements Array<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return this.size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -127,7 +128,7 @@ public class ArrayImpl<T> implements Array<T> {
|
|||||||
@Override
|
@Override
|
||||||
public T next() {
|
public T next() {
|
||||||
if (!hasNext())
|
if (!hasNext())
|
||||||
throw new java.util.NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
|
|
||||||
lastReturnedIndex = currentIndex;
|
lastReturnedIndex = currentIndex;
|
||||||
return (T) elements[currentIndex++];
|
return (T) elements[currentIndex++];
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ua.nure.jfn.task2;
|
package ua.nure.jfn.task2;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
public class ListImpl<T> implements List<T> {
|
public class ListImpl<T> implements List<T> {
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ public class ListImpl<T> implements List<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void removeFirst() {
|
public void removeFirst() {
|
||||||
if (head == null)
|
if (head == null)
|
||||||
throw new java.util.NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
|
|
||||||
head = head.next;
|
head = head.next;
|
||||||
size--;
|
size--;
|
||||||
@@ -123,7 +124,7 @@ public class ListImpl<T> implements List<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void removeLast() {
|
public void removeLast() {
|
||||||
if (head == null)
|
if (head == null)
|
||||||
throw new java.util.NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
|
|
||||||
if (head.isLast())
|
if (head.isLast())
|
||||||
head = null;
|
head = null;
|
||||||
@@ -137,7 +138,7 @@ public class ListImpl<T> implements List<T> {
|
|||||||
@Override
|
@Override
|
||||||
public T getFirst() {
|
public T getFirst() {
|
||||||
if (head == null)
|
if (head == null)
|
||||||
throw new java.util.NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
|
|
||||||
return (T) head.data;
|
return (T) head.data;
|
||||||
}
|
}
|
||||||
@@ -146,11 +147,11 @@ public class ListImpl<T> implements List<T> {
|
|||||||
@Override
|
@Override
|
||||||
public T getLast() {
|
public T getLast() {
|
||||||
if (head == null)
|
if (head == null)
|
||||||
throw new java.util.NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
|
|
||||||
if (head.isLast())
|
if (head.isLast())
|
||||||
return (T) head.data;
|
return (T) head.data;
|
||||||
else
|
|
||||||
return (T) head.getLast();
|
return (T) head.getLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +163,8 @@ public class ListImpl<T> implements List<T> {
|
|||||||
|
|
||||||
if (head.data != null && head.data.equals(element))
|
if (head.data != null && head.data.equals(element))
|
||||||
return (T) head.data;
|
return (T) head.data;
|
||||||
else if (head.next != null)
|
|
||||||
|
if (head.next != null)
|
||||||
return (T) head.search(element);
|
return (T) head.search(element);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -178,9 +180,8 @@ public class ListImpl<T> implements List<T> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (head.next != null) {
|
if (head.next != null)
|
||||||
return head.remove(element);
|
return head.remove(element);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -224,7 +225,7 @@ public class ListImpl<T> implements List<T> {
|
|||||||
@Override
|
@Override
|
||||||
public T next() {
|
public T next() {
|
||||||
if (!hasNext())
|
if (!hasNext())
|
||||||
throw new java.util.NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
|
|
||||||
prev = curr;
|
prev = curr;
|
||||||
curr = curr.next;
|
curr = curr.next;
|
||||||
|
|||||||
@@ -20,10 +20,9 @@ public class StreamImpl<T> implements Stream<T> {
|
|||||||
public Container<?> apply(Container<?> input) {
|
public Container<?> apply(Container<?> input) {
|
||||||
Array<T> newContainer = new ArrayImpl<T>();
|
Array<T> newContainer = new ArrayImpl<T>();
|
||||||
|
|
||||||
for (Object element : input) {
|
for (Object element : input)
|
||||||
if (predicate.apply((T) element))
|
if (predicate.apply((T) element))
|
||||||
newContainer.add((T) element);
|
newContainer.add((T) element);
|
||||||
}
|
|
||||||
|
|
||||||
return newContainer;
|
return newContainer;
|
||||||
}
|
}
|
||||||
@@ -41,9 +40,8 @@ public class StreamImpl<T> implements Stream<T> {
|
|||||||
public Container<?> apply(Container<?> input) {
|
public Container<?> apply(Container<?> input) {
|
||||||
Array<R> newContainer = new ArrayImpl<R>();
|
Array<R> newContainer = new ArrayImpl<R>();
|
||||||
|
|
||||||
for (Object element : input) {
|
for (Object element : input)
|
||||||
newContainer.add(function.apply((T) element));
|
newContainer.add(function.apply((T) element));
|
||||||
}
|
|
||||||
|
|
||||||
return newContainer;
|
return newContainer;
|
||||||
}
|
}
|
||||||
@@ -61,14 +59,14 @@ public class StreamImpl<T> implements Stream<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<T> filter(Function<? super T, Boolean> predicate) {
|
public Stream<T> filter(Function<? super T, Boolean> predicate) {
|
||||||
Array<Operation> newOperations = new ArrayImpl<>(this.operations);
|
Array<Operation> newOperations = new ArrayImpl<>(operations);
|
||||||
newOperations.add(new FilterOperation<>(predicate));
|
newOperations.add(new FilterOperation<>(predicate));
|
||||||
return new StreamImpl<T>(source, newOperations);
|
return new StreamImpl<T>(source, newOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R> Stream<R> map(Function<? super T, ? extends R> function) {
|
public <R> Stream<R> map(Function<? super T, ? extends R> function) {
|
||||||
Array<Operation> newOperations = new ArrayImpl<>(this.operations);
|
Array<Operation> newOperations = new ArrayImpl<>(operations);
|
||||||
newOperations.add(new MapOperation<>(function));
|
newOperations.add(new MapOperation<>(function));
|
||||||
return new StreamImpl<R>(source, newOperations);
|
return new StreamImpl<R>(source, newOperations);
|
||||||
}
|
}
|
||||||
@@ -88,13 +86,10 @@ public class StreamImpl<T> implements Stream<T> {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public void forEach(Action<? super T> action) {
|
public void forEach(Action<? super T> action) {
|
||||||
for (Operation operation : operations) {
|
for (Operation operation : operations)
|
||||||
source = operation.apply(source);
|
source = operation.apply(source);
|
||||||
}
|
|
||||||
|
|
||||||
for (Object element : source) {
|
for (Object element : source)
|
||||||
action.perform((T) element);
|
action.perform((T) element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
> [!NOTE]
|
||||||
|
> Викладач: Колесников Д. О.
|
||||||
|
>
|
||||||
|
> Оцінка: In Progress
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
@@ -0,0 +1,202 @@
|
|||||||
|
/target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/git,java,maven,eclipse,windows
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
### Eclipse Patch ###
|
||||||
|
# Eclipse Core
|
||||||
|
.project
|
||||||
|
|
||||||
|
# JDT-specific (Eclipse Java Development Tools)
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated
|
||||||
|
|
||||||
|
.sts4-cache/
|
||||||
|
|
||||||
|
### Git ###
|
||||||
|
# Created by git for backups. To disable backups in Git:
|
||||||
|
# $ git config --global mergetool.keepBackup false
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# Created by git when using merge tools for conflicts
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
*.REMOTE.*
|
||||||
|
*_BACKUP_*.txt
|
||||||
|
*_BASE_*.txt
|
||||||
|
*_LOCAL_*.txt
|
||||||
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### Maven ###
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Some additional ignores (sort later)
|
||||||
|
*.DS_Store
|
||||||
|
*.sw?
|
||||||
|
.#*
|
||||||
|
*#
|
||||||
|
*~
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
bin
|
||||||
|
build
|
||||||
|
target
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
*.sublime-*
|
||||||
|
/scratch
|
||||||
|
.gradle
|
||||||
|
README.html
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
.exercism
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàð³ÿ Ïåòðåíêî;petrenko2@mail.com
|
||||||
|
ivanenko;Áåàòðèññà-Âàëåði ²âàíåíêî;ivanenko@mail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
lennon;John Lennon;lennon@live.com
|
||||||
|
bush;Äæîðäæ Âîêåð Áóø;bush@gmail.com
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
When I was younger, so much younger than today
|
||||||
|
I never needed anybody's help in any way
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
when I was younger younger than today was
|
||||||
|
I never needed needed never never was I was
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>ua.nure.jfn</groupId>
|
||||||
|
<artifactId>task3</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.release>17</maven.compiler.release>
|
||||||
|
<junit5.version>5.12.0</junit5.version>
|
||||||
|
<surefire.version>3.5.2</surefire.version>
|
||||||
|
<spoon.version>11.2.0</spoon.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>${junit5.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
|
<version>${junit5.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>fr.inria.gforge.spoon</groupId>
|
||||||
|
<artifactId>spoon-core</artifactId>
|
||||||
|
<version>${spoon.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<version>2.0.17</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>task3</finalName>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>${surefire.version}</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import ua.nure.jfn.task3.converter.CyrillicConverter;
|
||||||
|
import ua.nure.jfn.task3.converter.MayaConverter;
|
||||||
|
import ua.nure.jfn.task3.converter.PositionalConverter;
|
||||||
|
import ua.nure.jfn.task3.converter.RomanConverter;
|
||||||
|
|
||||||
|
public class Demo {
|
||||||
|
|
||||||
|
private static final String[] EMPTY = new String[] {};
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("----Part 1----");
|
||||||
|
Part1.main(EMPTY);
|
||||||
|
|
||||||
|
System.out.println("----Part 2----");
|
||||||
|
Part2.main(EMPTY);
|
||||||
|
|
||||||
|
System.out.println("----Part 3----");
|
||||||
|
Part3.main(EMPTY);
|
||||||
|
|
||||||
|
String s;
|
||||||
|
|
||||||
|
System.out.println("----Cyrillic----");
|
||||||
|
s = "999_999";
|
||||||
|
System.out.println(s);
|
||||||
|
System.out.println(CyrillicConverter.convert(s));
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("----Maya----");
|
||||||
|
s = "100_000";
|
||||||
|
System.out.println(s);
|
||||||
|
System.out.println(MayaConverter.convert(s));
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("----Roman----");
|
||||||
|
s = "3_999";
|
||||||
|
System.out.println(s);
|
||||||
|
System.out.println(RomanConverter.convert(s));
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("----Positional----");
|
||||||
|
PositionalConverter.main(EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Part1 {
|
||||||
|
|
||||||
|
private static final String PATH = "part1.txt";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String lines = Utils.getContent(PATH);
|
||||||
|
System.out.println(convert1(lines));
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(convert2(lines));
|
||||||
|
System.out.println();
|
||||||
|
System.out.println(convert3(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convert1(String input) {
|
||||||
|
Matcher matcher = Pattern.compile(" ([\\S]*;[\\S]*@[\\S]*)").matcher(input);
|
||||||
|
|
||||||
|
String result = "LastName;Email";
|
||||||
|
while (matcher.find())
|
||||||
|
result += "\n" + matcher.group(1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convert2(String input) {
|
||||||
|
Matcher matcher = Pattern.compile(";([\\S]*) *([\\S]*) ([\\S]*);").matcher(input);
|
||||||
|
|
||||||
|
String result = "LastName;MiddleName;FirstName";
|
||||||
|
while (matcher.find())
|
||||||
|
result += String.format(
|
||||||
|
"\n%s;%s;%s",
|
||||||
|
matcher.group(3),
|
||||||
|
matcher.group(2),
|
||||||
|
matcher.group(1));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convert3(String input) {
|
||||||
|
Matcher matcher = Pattern.compile("(.*);(.*) ([^\\s]*);").matcher(input);
|
||||||
|
|
||||||
|
int[] len = { 0, 0, 0 };
|
||||||
|
while (matcher.find())
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
len[i] = Utils.max(len[i], matcher.group(i + 1).length());
|
||||||
|
|
||||||
|
matcher.reset();
|
||||||
|
|
||||||
|
String result = "";
|
||||||
|
while (matcher.find())
|
||||||
|
result += String.format(
|
||||||
|
"\n%-" + len[2] + "s %-" + len[1] + "s %-" + len[0] + "s",
|
||||||
|
matcher.group(3),
|
||||||
|
matcher.group(2),
|
||||||
|
matcher.group(1));
|
||||||
|
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Part2 {
|
||||||
|
|
||||||
|
private static final String PATH = "part2.txt";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String lines = Utils.getContent(PATH);
|
||||||
|
for (int j = 1; j < 10; j++)
|
||||||
|
System.out.println(convert(lines, j));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convert(String input, int k) {
|
||||||
|
int lengths = 0;
|
||||||
|
|
||||||
|
for (String word : input.split(" "))
|
||||||
|
lengths |= 1 << (word.length() - 1);
|
||||||
|
|
||||||
|
int kLength = 0;
|
||||||
|
int currLength = 0;
|
||||||
|
|
||||||
|
for (; lengths > 0 && currLength != k; lengths >>= 1) {
|
||||||
|
kLength++;
|
||||||
|
|
||||||
|
if ((lengths & 1) == 1)
|
||||||
|
currLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currLength != k)
|
||||||
|
kLength = 0;
|
||||||
|
|
||||||
|
Matcher matcher = Pattern.compile(String.format("(?<=^|\\s)(\\S{%d})(?=$|\\s)", kLength)).matcher(input);
|
||||||
|
|
||||||
|
String result = "";
|
||||||
|
while (matcher.find())
|
||||||
|
if (!result.matches(".*(?<=^|\\s)" + matcher.group(1) + "(?=$|\\s).*"))
|
||||||
|
result += " " + matcher.group(1);
|
||||||
|
|
||||||
|
if (result != "")
|
||||||
|
result = k + ":" + result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Part3 {
|
||||||
|
|
||||||
|
private static final String PATH = "part3.txt";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String lines = Utils.getContent(PATH);
|
||||||
|
System.out.println(convert(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convert(String input) {
|
||||||
|
String[] words = input.split("\\s+");
|
||||||
|
String result = input;
|
||||||
|
|
||||||
|
int last = 0;
|
||||||
|
String[] processed = new String[words.length];
|
||||||
|
for (String word : words) {
|
||||||
|
if (Utils.contains(word, processed))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile("(?<=^|\\s)(\\Q" + word + "\\E)(?=$|\\s)",
|
||||||
|
Pattern.UNICODE_CHARACTER_CLASS);
|
||||||
|
|
||||||
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
for (int i = 0; matcher.find(); i++) {
|
||||||
|
if (i % 2 != 0)
|
||||||
|
result = Utils.copyOfRange(result, 0, matcher.start()) + Utils.changeCase(matcher.group())
|
||||||
|
+ Utils.copyOfRange(result, matcher.end(), input.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
processed[last++] = word;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public final class Utils {
|
||||||
|
|
||||||
|
private static final String ENCODING = "Cp1251";
|
||||||
|
|
||||||
|
private Utils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int parseInt(String s, int base) {
|
||||||
|
int num = 0;
|
||||||
|
int pow = 1;
|
||||||
|
|
||||||
|
char[] chArray = s.toCharArray();
|
||||||
|
for (int i = chArray.length - 1; i >= 0; i--) {
|
||||||
|
char ch = chArray[i];
|
||||||
|
if (ch >= '0' && ch <= '9') {
|
||||||
|
num += (ch - '0') * pow;
|
||||||
|
pow *= base;
|
||||||
|
} else if (ch >= 'A' && ch <= 'Z') {
|
||||||
|
num += (ch - 'A' + 10) * pow;
|
||||||
|
pow *= base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int parseInt(String s) {
|
||||||
|
return parseInt(s, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String changeCase(String s) {
|
||||||
|
String arr1 = "abcdefghijklmnopqrstuvwxyzабвгґдеєжзиіїйклмнопрстуфхцчшщьюяABCDEFGHIJKLMNOPQRSTUVWXYZАБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ";
|
||||||
|
String arr2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯabcdefghijklmnopqrstuvwxyzабвгґдеєжзиіїйклмнопрстуфхцчшщьюя";
|
||||||
|
|
||||||
|
String result = "";
|
||||||
|
|
||||||
|
for (char c : s.toCharArray()) {
|
||||||
|
int i = arr1.indexOf(c);
|
||||||
|
if (i != -1)
|
||||||
|
c = arr2.charAt(i);
|
||||||
|
|
||||||
|
result += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean contains(String o, String[] arr) {
|
||||||
|
for (String e : arr)
|
||||||
|
if (e != null && e.equals(o))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String copyOfRange(String s, int start, int end) {
|
||||||
|
char[] arr = s.toCharArray();
|
||||||
|
|
||||||
|
char[] result = new char[end - start];
|
||||||
|
for (int i = 0; i < end - start; i++)
|
||||||
|
result[i] = arr[start + i];
|
||||||
|
|
||||||
|
return new String(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int max(int a, int b) {
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getContent(String path) {
|
||||||
|
String res = null;
|
||||||
|
try {
|
||||||
|
byte[] bytes = Files.readAllBytes(Paths.get(path));
|
||||||
|
res = new String(bytes, ENCODING);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(getContent("part1.txt"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package ua.nure.jfn.task3.converter;
|
||||||
|
|
||||||
|
import ua.nure.jfn.task3.Utils;
|
||||||
|
|
||||||
|
// Cyrillic numerals.
|
||||||
|
public class CyrillicConverter {
|
||||||
|
|
||||||
|
private static char[][] digits = {
|
||||||
|
{ '\u0430', '\u0432', '\u0433', '\u0434', '\u0454', '\u0455', '\u0437', '\u0438', '\u0473' },
|
||||||
|
{ '\u0456', '\u043A', '\u043B', '\u043C', '\u043D', '\u046F', '\u043E', '\u043F', '\u0447' },
|
||||||
|
{ '\u0440', '\u0441', '\u0442', '\u0443', '\u0444', '\u0445', '\u0471', '\u0461', '\u0446' }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The thousands sign to multiply the number's value.
|
||||||
|
*/
|
||||||
|
private static char kMul = '\u0482';
|
||||||
|
|
||||||
|
public static String convert(String str) {
|
||||||
|
int num = Utils.parseInt(str);
|
||||||
|
String result = "";
|
||||||
|
|
||||||
|
boolean isK = false;
|
||||||
|
int digitPlace = 0;
|
||||||
|
|
||||||
|
while (num != 0) {
|
||||||
|
int digit = (num % 10);
|
||||||
|
|
||||||
|
if (digit > 0)
|
||||||
|
result = String.format("%s%s%s", isK ? kMul : "", digits[digitPlace][digit - 1], result);
|
||||||
|
|
||||||
|
num /= 10;
|
||||||
|
isK = digitPlace == 2 ? true : isK;
|
||||||
|
digitPlace += digitPlace == 2 ? -2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("All the digits:");
|
||||||
|
int k = 1;
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
System.out.printf("%3s: %s [%s, %s]%n", (i + 1) * k, digits[j][i], j, i);
|
||||||
|
}
|
||||||
|
k *= 10;
|
||||||
|
}
|
||||||
|
System.out.println(kMul);
|
||||||
|
String s = "999_999";
|
||||||
|
System.out.printf("Maximum value (%s):%n", s);
|
||||||
|
System.out.println(CyrillicConverter.convert(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package ua.nure.jfn.task3.converter;
|
||||||
|
|
||||||
|
import ua.nure.jfn.task3.Utils;
|
||||||
|
|
||||||
|
// Maya numerals.
|
||||||
|
// You can use Google Font: "Noto Sans Mayan Numerals" to see the Mayan digits.
|
||||||
|
public class MayaConverter {
|
||||||
|
|
||||||
|
private static final String[] ar = new String[20];
|
||||||
|
|
||||||
|
static {
|
||||||
|
int mayaZeroHighSurrogate = 0xDEE0;
|
||||||
|
for (int j = 0; j < 20; j++) {
|
||||||
|
ar[j] = "\uD834" + (char) (mayaZeroHighSurrogate + j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convert(String from) {
|
||||||
|
int num = Utils.parseInt(from);
|
||||||
|
String result = "";
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
result = ar[0];
|
||||||
|
|
||||||
|
for (; num > 0; num /= 20)
|
||||||
|
result = ar[num % 20] + result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("All the digits:");
|
||||||
|
for (int j = 0; j < ar.length; j++) {
|
||||||
|
System.out.printf("%2s: %s%n", j, ar[j]);
|
||||||
|
}
|
||||||
|
System.out.println("~~~");
|
||||||
|
System.out.println(429);
|
||||||
|
System.out.println(MayaConverter.convert("429"));
|
||||||
|
System.out.println("~~~");
|
||||||
|
System.out.println("100_000");
|
||||||
|
System.out.println(MayaConverter.convert(String.valueOf("100_000")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package ua.nure.jfn.task3.converter;
|
||||||
|
|
||||||
|
import ua.nure.jfn.task3.Utils;
|
||||||
|
|
||||||
|
public class PositionalConverter {
|
||||||
|
|
||||||
|
public static String convert(String s) {
|
||||||
|
String[] parts = s.split(":");
|
||||||
|
|
||||||
|
int from = Utils.parseInt(parts[0]);
|
||||||
|
int num = Utils.parseInt(parts[1], from);
|
||||||
|
int to = Utils.parseInt(parts[2]);
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
return "0";
|
||||||
|
|
||||||
|
String result = "";
|
||||||
|
for (; num > 0; num /= to) {
|
||||||
|
char c = (char) (num % to);
|
||||||
|
|
||||||
|
if (c <= 9) {
|
||||||
|
c += '0';
|
||||||
|
} else {
|
||||||
|
c += 'A' - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = c + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String s;
|
||||||
|
|
||||||
|
s = "10:15:16";
|
||||||
|
System.out.printf("%s ==> %s%n", s, PositionalConverter.convert(s));
|
||||||
|
s = "36:XYZ:2";
|
||||||
|
System.out.printf("%s ==> %s%n", s, PositionalConverter.convert(s));
|
||||||
|
s = "10:171:36";
|
||||||
|
System.out.printf("%s ==> %s%n", s, PositionalConverter.convert(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
package ua.nure.jfn.task3.converter;
|
||||||
|
|
||||||
|
import ua.nure.jfn.task3.Utils;
|
||||||
|
|
||||||
|
// Roman numerals.
|
||||||
|
public class RomanConverter {
|
||||||
|
|
||||||
|
private static final String[] digits = {
|
||||||
|
"\u216F", // M
|
||||||
|
"\u216D\u216F", // CM
|
||||||
|
"\u216E", // D
|
||||||
|
"\u216D\u216E", // CD
|
||||||
|
"\u216D", // C
|
||||||
|
"\u2169\u216D", // XC
|
||||||
|
"\u216C", // L
|
||||||
|
"\u2169\u216C", // XL
|
||||||
|
"\u2169", // X
|
||||||
|
"\u2160\u2169", // IX
|
||||||
|
"\u2164", // V
|
||||||
|
"\u2160\u2164", // IV
|
||||||
|
"\u2160" // I
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int[] values = {
|
||||||
|
1000, // M
|
||||||
|
900, // CM
|
||||||
|
500, // D
|
||||||
|
400, // CD
|
||||||
|
100, // C
|
||||||
|
90, // XC
|
||||||
|
50, // L
|
||||||
|
40, // XL
|
||||||
|
10, // X
|
||||||
|
9, // IX
|
||||||
|
5, // V
|
||||||
|
4, // IV
|
||||||
|
1 // I
|
||||||
|
};
|
||||||
|
|
||||||
|
public static String convert(String str) {
|
||||||
|
int num = Utils.parseInt(str);
|
||||||
|
String result = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
while (num >= values[i]) {
|
||||||
|
num -= values[i];
|
||||||
|
result += digits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("All the digits");
|
||||||
|
for (int j = 0; j < digits.length; j++) {
|
||||||
|
System.out.printf("%s: %s%n", digits[j], values[j]);
|
||||||
|
}
|
||||||
|
String s;
|
||||||
|
System.out.println("~~~");
|
||||||
|
s = "444";
|
||||||
|
System.out.println(s);
|
||||||
|
System.out.println(RomanConverter.convert(s));
|
||||||
|
System.out.println("~~~");
|
||||||
|
s = "3_999";
|
||||||
|
System.out.println(s);
|
||||||
|
System.out.println(RomanConverter.convert(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
public class Base {
|
||||||
|
|
||||||
|
{
|
||||||
|
if (ComplianceTest.MAKE_ALL_TESTS_FAILED) {
|
||||||
|
Assertions.fail("Compliance tests have not been passed", ComplianceTest.CAUSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import spoon.Launcher;
|
||||||
|
import spoon.SpoonAPI;
|
||||||
|
import spoon.reflect.declaration.CtType;
|
||||||
|
import spoon.reflect.reference.CtTypeReference;
|
||||||
|
import spoon.reflect.visitor.filter.TypeFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
@Disabled("This test is used as a trigger to fail all the other tests")
|
||||||
|
class ComplianceTest {
|
||||||
|
|
||||||
|
// Assign this option to false to skip the compliance test
|
||||||
|
// Note, during testing at the stand this option will be turned on!!!
|
||||||
|
// private static final boolean TURN_TEST_COMPLIANCE_ON = false;
|
||||||
|
private static final boolean TURN_TEST_COMPLIANCE_ON = true;
|
||||||
|
|
||||||
|
public static final boolean MAKE_ALL_TESTS_FAILED;
|
||||||
|
|
||||||
|
public static final Throwable CAUSE;
|
||||||
|
|
||||||
|
private static final Object EOL = System.lineSeparator();
|
||||||
|
|
||||||
|
static {
|
||||||
|
L: {
|
||||||
|
try {
|
||||||
|
if (TURN_TEST_COMPLIANCE_ON) {
|
||||||
|
initSpoon();
|
||||||
|
startCompianceTests();
|
||||||
|
}
|
||||||
|
} catch (ReflectiveOperationException ex) {
|
||||||
|
MAKE_ALL_TESTS_FAILED = true;
|
||||||
|
CAUSE = ex.getCause();
|
||||||
|
break L;
|
||||||
|
}
|
||||||
|
MAKE_ALL_TESTS_FAILED = false;
|
||||||
|
CAUSE = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SpoonAPI spoon;
|
||||||
|
|
||||||
|
private static void initSpoon() {
|
||||||
|
spoon = new Launcher();
|
||||||
|
spoon.addInputResource("src/main/java/");
|
||||||
|
spoon.buildModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void startCompianceTests() throws ReflectiveOperationException {
|
||||||
|
ComplianceTest cTest = new ComplianceTest();
|
||||||
|
for (Method m : ComplianceTest.class.getDeclaredMethods()) {
|
||||||
|
if (Modifier.isPrivate(m.getModifiers())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Test[] ar = m.getAnnotationsByType(Test.class);
|
||||||
|
if (ar.length > 0 && m.getAnnotationsByType(Test.class)[0] != null) {
|
||||||
|
m.invoke(cTest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void appShouldNotUseForbiddenAPI() throws IOException, URISyntaxException {
|
||||||
|
URL url = getClass().getResource("forbidden-api-regex.txt");
|
||||||
|
String regex = Files.readString(Path.of(url.toURI()));
|
||||||
|
Pattern forbiddenAPIRegex = Pattern.compile(regex.toString());
|
||||||
|
StringBuilder errorMessage = new StringBuilder();
|
||||||
|
for (CtType<?> ctType : spoon.getModel().getAllTypes()) {
|
||||||
|
List<String> forbiddenAPI = ctType.getElements(new TypeFilter<>(CtTypeReference.class))
|
||||||
|
.stream()
|
||||||
|
.distinct()
|
||||||
|
.filter(r -> forbiddenAPIRegex.matcher(r.toString()).matches())
|
||||||
|
.map(CtTypeReference::getQualifiedName)
|
||||||
|
.toList();
|
||||||
|
if (!forbiddenAPI.isEmpty()) {
|
||||||
|
errorMessage.append(EOL)
|
||||||
|
.append(ctType.getQualifiedName()).append(": ")
|
||||||
|
.append(forbiddenAPI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!errorMessage.isEmpty()) {
|
||||||
|
fail(() -> "Using of this API is forbidden: " + errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldBeAppropriateNumberOfPackagesAndClasses() throws IOException, URISyntaxException {
|
||||||
|
URL url = getClass().getResource("list-of-types.txt");
|
||||||
|
String expected = Files.readString(Path.of(url.toURI()));
|
||||||
|
// '\n' character is used for clarity in error message
|
||||||
|
String actual = spoon.getModel().getAllPackages().stream()
|
||||||
|
.filter(p -> p.getTypes().size() != 0)
|
||||||
|
.map(p -> p.getTypes().stream()
|
||||||
|
.map(CtType::getQualifiedName)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.joining("\n")))
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
assertEquals('\n' + expected.trim(), '\n' + actual.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
class Part1Test extends Base {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "Cp1251", resources = "part1-convert1.csv")
|
||||||
|
void testConvert1(String inputString, String expectedString) {
|
||||||
|
String expected = expectedString.replace("~", "\n");
|
||||||
|
String input = inputString.replace("~", "\n");
|
||||||
|
String actual = Part1.convert1(input).replaceAll("\r", "");
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "Cp1251", resources = "part1-convert2.csv")
|
||||||
|
void testConvert2(String inputString, String expectedString) {
|
||||||
|
String expected = expectedString.replace("~", "\n");
|
||||||
|
String input = inputString.replace("~", "\n");
|
||||||
|
String actual = Part1.convert2(input).replaceAll("\r", "");
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "Cp1251", resources = "part1-convert3.csv")
|
||||||
|
void testConvert3(String inputString, String expectedString) {
|
||||||
|
String expected = expectedString.replace("~", "\n");
|
||||||
|
String input = inputString.replace("~", "\n");
|
||||||
|
String actual = Part1.convert3(input).replaceAll("\r", "");
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
class Part2Test extends Base {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "Cp1251", resources = "part2.csv")
|
||||||
|
void testConvert(String inputString, String kString, String expectedString) {
|
||||||
|
String expected = expectedString != null ? expectedString.replace("~", "\n") : "";
|
||||||
|
int k = Integer.parseInt(kString);
|
||||||
|
String input = inputString.replace("~", "\n");
|
||||||
|
String actual = Part2.convert(input, k).replaceAll("\r", "");
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package ua.nure.jfn.task3;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
class Part3Test extends Base {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "Cp1251", resources = "part3.csv")
|
||||||
|
void testConvert(String inputString, String expectedString) {
|
||||||
|
String expected = expectedString.replace("~", "\n");
|
||||||
|
String input = inputString.replace("~", "\n");
|
||||||
|
String actual = Part3.convert(input).replaceAll("\r", "");
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package ua.nure.jfn.task3.converter;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
import ua.nure.jfn.task3.Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
public class ConverterTest extends Base {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "UTF-8", resources = "cyrillic.csv")
|
||||||
|
void testCyrillicConvert(String input, String expected) {
|
||||||
|
String actual = CyrillicConverter.convert(input);
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "UTF-8", resources = "positional.csv")
|
||||||
|
void testPositionalConvert(String input, String expected) {
|
||||||
|
String actual = PositionalConverter.convert(input);
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "UTF-8", resources = "maya.csv")
|
||||||
|
void testMayanConvert(String input, String expected) {
|
||||||
|
String actual = MayaConverter.convert(input);
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(delimiter = '|', encoding = "UTF-8", resources = "roman.csv")
|
||||||
|
void testRomanConvert(String input, String expected) {
|
||||||
|
String actual = RomanConverter.convert(input);
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# To see Cyrillic digits you can use 'Consolas' font
|
||||||
|
|
||||||
|
1 | а
|
||||||
|
10 | і
|
||||||
|
19 | іѳ
|
||||||
|
20 | к
|
||||||
|
901 | ца
|
||||||
|
410 | уі
|
||||||
|
80_030 | ҂пл
|
||||||
|
999_999 | ҂ц҂ч҂ѳцчѳ
|
||||||
|
987_321 | ҂ц҂п҂зтка
|
||||||
|
100_290 | ҂рсч
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
# To see Mayan digits you can use 'BabelStone Mayan Numerals' font
|
||||||
|
|
||||||
|
429 | 𝋡𝋡𝋩
|
||||||
|
100_000 | 𝋬𝋪𝋠𝋠
|
||||||
|
2147483647 | 𝋡𝋭𝋫𝋡𝋯𝋩𝋢𝋧
|
||||||
|
1 | 𝋡
|
||||||
|
202020 | 𝋡𝋥𝋥𝋡𝋠
|
||||||
|
19 | 𝋳
|
||||||
|
20 | 𝋡𝋠
|
||||||
|
3_999 | 𝋩𝋳𝋳
|
||||||
|
399 | 𝋳𝋳
|
||||||
|
400 | 𝋡𝋠𝋠
|
||||||
|
+10
@@ -0,0 +1,10 @@
|
|||||||
|
10:429:36 | BX
|
||||||
|
15:10_000:4 | 30113001
|
||||||
|
10:2147483647:16 | 7FFFFFFF
|
||||||
|
10:1:36 | 1
|
||||||
|
3:202020:10 | 546
|
||||||
|
36:XYZ:2 | 1010101111111011
|
||||||
|
36:1:2 | 1
|
||||||
|
2:1:36 | 1
|
||||||
|
10:36:36 | 10
|
||||||
|
10:8:8 | 10
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
# To see Roman digits you can use 'Microsoft Sans Serif' font
|
||||||
|
|
||||||
|
3_999 | ⅯⅯⅯⅭⅯⅩⅭⅠⅩ
|
||||||
|
444 | ⅭⅮⅩⅬⅠⅤ
|
||||||
|
949 | ⅭⅯⅩⅬⅠⅩ
|
||||||
|
1 | Ⅰ
|
||||||
|
2949 | ⅯⅯⅭⅯⅩⅬⅠⅩ
|
||||||
|
4 | ⅠⅤ
|
||||||
|
3_201 | ⅯⅯⅯⅭⅭⅠ
|
||||||
|
1003 | ⅯⅠⅠⅠ
|
||||||
|
770 | ⅮⅭⅭⅬⅩⅩ
|
||||||
|
9 | ⅠⅩ
|
||||||
|
+8
@@ -0,0 +1,8 @@
|
|||||||
|
(?x)
|
||||||
|
^
|
||||||
|
(?:java\.util\.[^.]+)
|
||||||
|
|
|
||||||
|
(?:java\.util\.stream\.[^.]+)
|
||||||
|
|
|
||||||
|
(?:java\.lang\.(?:Byte|Short|Integer|Long|Float|Double|Character))
|
||||||
|
$
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ua.nure.jfn.task3.Demo
|
||||||
|
ua.nure.jfn.task3.Part1
|
||||||
|
ua.nure.jfn.task3.Part2
|
||||||
|
ua.nure.jfn.task3.Part3
|
||||||
|
ua.nure.jfn.task3.Utils
|
||||||
|
ua.nure.jfn.task3.converter.CyrillicConverter
|
||||||
|
ua.nure.jfn.task3.converter.MayaConverter
|
||||||
|
ua.nure.jfn.task3.converter.PositionalConverter
|
||||||
|
ua.nure.jfn.task3.converter.RomanConverter
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàð³ÿ Ïåòðåíêî;petrenko2@mail.com
|
||||||
|
ivanenko;Áåàòðèññà-Âàëåði ²âàíåíêî;ivanenko@mail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
lennon;John Lennon;lennon@live.com
|
||||||
|
bush;Äæîðäæ Âîêåð Áóø;bush@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;Email
|
||||||
|
Ïåòðåíêî;petrenko2@mail.com
|
||||||
|
²âàíåíêî;ivanenko@mail.com
|
||||||
|
Obama;obama@gmail.com
|
||||||
|
Lennon;lennon@live.com
|
||||||
|
Áóø;bush@gmail.com
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàð³ÿ Ïåòðåíêî;petrenko2@mail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;Email
|
||||||
|
Ïåòðåíêî;petrenko2@mail.com
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàð³ÿ Ïåòðåíêî;petrenko2@mail.com
|
||||||
|
ivanenko;Áåàòðèññà ²âàííà ²âàíåíêî;ivanenko@mail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;Email
|
||||||
|
Ïåòðåíêî;petrenko2@mail.com
|
||||||
|
²âàíåíêî;ivanenko@mail.com
|
||||||
|
Obama;obama@gmail.com
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;Email
|
||||||
|
Obama;obama@gmail.com
|
||||||
|
Obama;obama@gmail.com
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
a;b c d;e@f.g
|
||||||
|
h;i j k;l@n.n
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;Email
|
||||||
|
d;e@f.g
|
||||||
|
k;l@n.n
|
||||||
|
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Login;Name;Email~petrenko;Ìàð³ÿ Ïåòðåíêî;petrenko2@mail.com~ivanenko;Áåàòðèññà-Âàëåði ²âàíåíêî;ivanenko@mail.com~obama;Barack Hussein Obama;obama@gmail.com~lennon;John Lennon;lennon@live.com~bush;Äæîðäæ Âîêåð Áóø;bush@gmail.com|LastName;Email~Ïåòðåíêî;petrenko2@mail.com~²âàíåíêî;ivanenko@mail.com~Obama;obama@gmail.com~Lennon;lennon@live.com~Áóø;bush@gmail.com
|
||||||
|
Login;Name;Email~petrenko;Ìàð³ÿ Ïåòðåíêî;petrenko2@mail.com|LastName;Email~Ïåòðåíêî;petrenko2@mail.com
|
||||||
|
Login;Name;Email~petrenko;Ìàð³ÿ Ïåòðåíêî;petrenko2@mail.com~ivanenko;Áåàòðèññà ²âàííà ²âàíåíêî;ivanenko@mail.com~obama;Barack Hussein Obama;obama@gmail.com|LastName;Email~Ïåòðåíêî;petrenko2@mail.com~²âàíåíêî;ivanenko@mail.com~Obama;obama@gmail.com
|
||||||
|
Login;Name;Email~obama;Barack Hussein Obama;obama@gmail.com~obama;Barack Hussein Obama;obama@gmail.com|LastName;Email~Obama;obama@gmail.com~Obama;obama@gmail.com
|
||||||
|
Login;Name;Email~a;b c d;e@f.g~h;i j k;l@n.n|LastName;Email~d;e@f.g~k;l@n.n
|
||||||
|
+56
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com
|
||||||
|
ivanenko;Áåàòğèññà-Âàëåği ²âàíåíêî;ivanenko@mail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
lennon;John Lennon;lennon@live.com
|
||||||
|
bush;Äæîğäæ Âîêåğ Áóø;bush@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;MiddleName;FirstName
|
||||||
|
Ïåòğåíêî;;Ìàğ³ÿ
|
||||||
|
²âàíåíêî;;Áåàòğèññà-Âàëåği
|
||||||
|
Obama;Hussein;Barack
|
||||||
|
Lennon;;John
|
||||||
|
Áóø;Âîêåğ;Äæîğäæ
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;MiddleName;FirstName
|
||||||
|
Ïåòğåíêî;;Ìàğ³ÿ
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com
|
||||||
|
ivanenko;Áåàòğèññà ²âàííà ²âàíåíêî;ivanenko@mail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;MiddleName;FirstName
|
||||||
|
Ïåòğåíêî;;Ìàğ³ÿ
|
||||||
|
²âàíåíêî;²âàííà;Áåàòğèññà
|
||||||
|
Obama;Hussein;Barack
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;MiddleName;FirstName
|
||||||
|
Obama;Hussein;Barack
|
||||||
|
Obama;Hussein;Barack
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
a;b c d;e@f.g
|
||||||
|
h;i j k;l@n.n
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LastName;MiddleName;FirstName
|
||||||
|
d;c;b
|
||||||
|
k;j;i
|
||||||
|
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Login;Name;Email~petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com~ivanenko;Áåàòğèññà-Âàëåği ²âàíåíêî;ivanenko@mail.com~obama;Barack Hussein Obama;obama@gmail.com~lennon;John Lennon;lennon@live.com~bush;Äæîğäæ Âîêåğ Áóø;bush@gmail.com|LastName;MiddleName;FirstName~Ïåòğåíêî;;Ìàğ³ÿ~²âàíåíêî;;Áåàòğèññà-Âàëåği~Obama;Hussein;Barack~Lennon;;John~Áóø;Âîêåğ;Äæîğäæ
|
||||||
|
Login;Name;Email~petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com|LastName;MiddleName;FirstName~Ïåòğåíêî;;Ìàğ³ÿ
|
||||||
|
Login;Name;Email~petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com~ivanenko;Áåàòğèññà ²âàííà ²âàíåíêî;ivanenko@mail.com~obama;Barack Hussein Obama;obama@gmail.com|LastName;MiddleName;FirstName~Ïåòğåíêî;;Ìàğ³ÿ~²âàíåíêî;²âàííà;Áåàòğèññà~Obama;Hussein;Barack
|
||||||
|
Login;Name;Email~obama;Barack Hussein Obama;obama@gmail.com~obama;Barack Hussein Obama;obama@gmail.com|LastName;MiddleName;FirstName~Obama;Hussein;Barack~Obama;Hussein;Barack
|
||||||
|
Login;Name;Email~a;b c d;e@f.g~h;i j k;l@n.n|LastName;MiddleName;FirstName~d;c;b~k;j;i
|
||||||
|
+51
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com
|
||||||
|
ivanenko;Áåàòğèññà-Âàëåği ²âàíåíêî;ivanenko@mail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
lennon;John Lennon;lennon@live.com
|
||||||
|
bush;Äæîğäæ Âîêåğ Áóø;bush@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Ïåòğåíêî Ìàğ³ÿ petrenko
|
||||||
|
²âàíåíêî Áåàòğèññà-Âàëåği ivanenko
|
||||||
|
Obama Barack Hussein obama
|
||||||
|
Lennon John lennon
|
||||||
|
Áóø Äæîğäæ Âîêåğ bush
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Ïåòğåíêî Ìàğ³ÿ petrenko
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com
|
||||||
|
ivanenko;Áåàòğèññà ²âàííà ²âàíåíêî;ivanenko@mail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Ïåòğåíêî Ìàğ³ÿ petrenko
|
||||||
|
²âàíåíêî Áåàòğèññà ²âàííà ivanenko
|
||||||
|
Obama Barack Hussein obama
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
obama;Barack Hussein Obama;obama@gmail.com
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Obama Barack Hussein obama
|
||||||
|
Obama Barack Hussein obama
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Login;Name;Email
|
||||||
|
a;b c d;e@f.g
|
||||||
|
h;i j k;l@n.n
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
d b c a
|
||||||
|
k i j h
|
||||||
|
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Login;Name;Email~petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com~ivanenko;Áåàòğèññà-Âàëåği ²âàíåíêî;ivanenko@mail.com~obama;Barack Hussein Obama;obama@gmail.com~lennon;John Lennon;lennon@live.com~bush;Äæîğäæ Âîêåğ Áóø;bush@gmail.com|Ïåòğåíêî Ìàğ³ÿ petrenko~²âàíåíêî Áåàòğèññà-Âàëåği ivanenko~Obama Barack Hussein obama ~Lennon John lennon ~Áóø Äæîğäæ Âîêåğ bush
|
||||||
|
Login;Name;Email~petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com|Ïåòğåíêî Ìàğ³ÿ petrenko
|
||||||
|
Login;Name;Email~petrenko;Ìàğ³ÿ Ïåòğåíêî;petrenko2@mail.com~ivanenko;Áåàòğèññà ²âàííà ²âàíåíêî;ivanenko@mail.com~obama;Barack Hussein Obama;obama@gmail.com|Ïåòğåíêî Ìàğ³ÿ petrenko~²âàíåíêî Áåàòğèññà ²âàííà ivanenko~Obama Barack Hussein obama
|
||||||
|
Login;Name;Email~obama;Barack Hussein Obama;obama@gmail.com~obama;Barack Hussein Obama;obama@gmail.com|Obama Barack Hussein obama~Obama Barack Hussein obama
|
||||||
|
Login;Name;Email~a;b c d;e@f.g~h;i j k;l@n.n|d b c a~k i j h
|
||||||
|
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
When I was younger, so much younger than today
|
||||||
|
I never needed anybody's help in any way
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
1
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
1: I
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
When I was younger, so much younger than today
|
||||||
|
I never needed anybody's help in any way
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
2
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
2: so in
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
When I was younger, so much younger than today
|
||||||
|
I never needed anybody's help in any way
|
||||||
|
Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³. Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
5
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
5: today never Ëÿãຠäåíü. íà䳿 íî÷³.
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
When I was younger, so much younger than today
|
||||||
|
I never needed anybody's help in any way
|
||||||
|
Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³. Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
6
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
6: needed â³ääຠâîãí³, ëàìïè.
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
When I was younger, so much younger than today
|
||||||
|
I never needed anybody's help in any way
|
||||||
|
Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³. Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
8
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
8: younger,
|
||||||
|
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
a b a abc bca abc abcde abcdf|1|1: a b
|
||||||
|
a b a abc bca abc abcde abcdf|2|2: abc bca
|
||||||
|
a b a abc bca abc abcde abcdf|3|3: abcde abcdf
|
||||||
|
a b a abc bca abc abcde abcdf|4|
|
||||||
|
When I was younger, so much younger than today~I never needed anybody's help in any way|1|1: I
|
||||||
|
When I was younger, so much younger than today~I never needed anybody's help in any way|2|2: so in
|
||||||
|
When I was younger, so much younger than today~I never needed anybody's help in any way~Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³. Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.|5|5: today never Ëÿãຠäåíü. íà䳿 íî÷³.
|
||||||
|
When I was younger, so much younger than today~I never needed anybody's help in any way~Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³. Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.|6|6: needed â³ääຠâîãí³, ëàìïè.
|
||||||
|
When I was younger, so much younger than today~I never needed anybody's help in any way~Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³. Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.|8|8: younger,
|
||||||
|
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
when I was younger younger than today was
|
||||||
|
I never needed needed never never was I was
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
when I was younger YOUNGER than today WAS
|
||||||
|
i never needed NEEDED NEVER never was I WAS
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³.
|
||||||
|
Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè.
|
||||||
|
Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.
|
||||||
|
Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.
|
||||||
|
Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè.
|
||||||
|
Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè.
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³.
|
||||||
|
Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè.
|
||||||
|
Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.
|
||||||
|
òÀÌ ÂÎÃͲ, ÿÑÊÐÀ² ÁËÈÙÓÒÜ ËÀÌÏÈ.
|
||||||
|
ðÎÁ²ÒÍÈÊÈ çÀÌÎÐÈËÈÑÜ ÏÐÀÖÞÂÀÒÈ.
|
||||||
|
Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè.
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
asdf adsf asdfa sd fa sdf adfa sdfa
|
||||||
|
asdfas dfasd
|
||||||
|
asd f
|
||||||
|
asdfasdfasdf
|
||||||
|
asdf asdf asdf asdf a sdf
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
asdf adsf asdfa sd fa sdf adfa sdfa
|
||||||
|
asdfas dfasd
|
||||||
|
asd f
|
||||||
|
asdfasdfasdf
|
||||||
|
ASDF asdf ASDF asdf a SDF
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
a b c a b c a b c
|
||||||
|
b c b c a b c a
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
a b c A B C a b c
|
||||||
|
B C b c A B C a
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
éöóê éóöê éóöê éöóê éóöê
|
||||||
|
éóöê éöóê éöóê éóöê éóöê
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
éöóê éóöê ÉÓÖÊ ÉÖÓÊ éóöê
|
||||||
|
ÉÓÖÊ éöóê ÉÖÓÊ éóöê ÉÓÖÊ
|
||||||
|
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
when I was younger younger than today was~I never needed needed never never was I was|when I was younger YOUNGER than today WAS~i never needed NEEDED NEVER never was I WAS
|
||||||
|
Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³. ~Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. ~Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.~Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.~Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. ~Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. |Ëÿãຠäåíü. ³í â³ääຠñâî¿ íà䳿 íî÷³. ~Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè. ~Òàì âîãí³, ßñêðàâ³ áëèùóòü ëàìïè.~òÀÌ ÂÎÃͲ, ÿÑÊÐÀ² ÁËÈÙÓÒÜ ËÀÌÏÈ.~ðÎÁ²ÒÍÈÊÈ çÀÌÎÐÈËÈÑÜ ÏÐÀÖÞÂÀÒÈ. ~Ðîá³òíèêè Çàìîðèëèñü ïðàöþâàòè.
|
||||||
|
asdf adsf asdfa sd fa sdf adfa sdfa ~asdfas dfasd~asd f~asdfasdfasdf~asdf asdf asdf asdf a sdf |asdf adsf asdfa sd fa sdf adfa sdfa ~asdfas dfasd~asd f~asdfasdfasdf~ASDF asdf ASDF asdf a SDF
|
||||||
|
a b c a b c a b c~b c b c a b c a |a b c A B C a b c~B C b c A B C a
|
||||||
|
éöóê éóöê éóöê éöóê éóöê~éóöê éöóê éöóê éóöê éóöê |éöóê éóöê ÉÓÖÊ ÉÖÓÊ éóöê~ÉÓÖÊ éöóê ÉÖÓÊ éóöê ÉÓÖÊ
|
||||||
|
Binary file not shown.
@@ -0,0 +1,202 @@
|
|||||||
|
/target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/git,java,maven,eclipse,windows
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
### Eclipse Patch ###
|
||||||
|
# Eclipse Core
|
||||||
|
.project
|
||||||
|
|
||||||
|
# JDT-specific (Eclipse Java Development Tools)
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated
|
||||||
|
|
||||||
|
.sts4-cache/
|
||||||
|
|
||||||
|
### Git ###
|
||||||
|
# Created by git for backups. To disable backups in Git:
|
||||||
|
# $ git config --global mergetool.keepBackup false
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# Created by git when using merge tools for conflicts
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
*.REMOTE.*
|
||||||
|
*_BACKUP_*.txt
|
||||||
|
*_BASE_*.txt
|
||||||
|
*_LOCAL_*.txt
|
||||||
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### Maven ###
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Some additional ignores (sort later)
|
||||||
|
*.DS_Store
|
||||||
|
*.sw?
|
||||||
|
.#*
|
||||||
|
*#
|
||||||
|
*~
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
bin
|
||||||
|
build
|
||||||
|
target
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
*.sublime-*
|
||||||
|
/scratch
|
||||||
|
.gradle
|
||||||
|
README.html
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
.exercism
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
set JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8
|
||||||
|
mvn clean test
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8
|
||||||
|
mvn clean test
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Сонце гріє лице,
|
||||||
|
Радує це!
|
||||||
|
Ooh
|
||||||
|
What more
|
||||||
|
can I say
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
asd asdf asd asdf
|
||||||
|
asdf 43 asdsf 43 43 434
|
||||||
|
stop
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>ua.nure.jfn</groupId>
|
||||||
|
<artifactId>task4</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.release>17</maven.compiler.release>
|
||||||
|
<junit5.version>5.12.0</junit5.version>
|
||||||
|
<surefire.version>3.5.3</surefire.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>${junit5.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
|
<version>${junit5.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>task4</finalName>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>${surefire.version}</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class Demo {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String symin;
|
||||||
|
InputStream stdin = System.in;
|
||||||
|
|
||||||
|
// Part1
|
||||||
|
symin = "asdf\n" +
|
||||||
|
"Latn\n" +
|
||||||
|
"Cyrl\n" +
|
||||||
|
"Stop\n";
|
||||||
|
System.setIn(new ByteArrayInputStream(symin.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
|
||||||
|
Part1.main(args);
|
||||||
|
// ~Part1
|
||||||
|
|
||||||
|
// Part3
|
||||||
|
symin = "table en\n" +
|
||||||
|
"table uk\n" +
|
||||||
|
"apple en\n" +
|
||||||
|
"asfd asdf\n" +
|
||||||
|
"apple uk\n" +
|
||||||
|
"stop\n";
|
||||||
|
System.setIn(new ByteArrayInputStream(symin.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
|
||||||
|
Part3.main(args);
|
||||||
|
// ~Part3
|
||||||
|
|
||||||
|
System.setIn(stdin);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Part1 {
|
||||||
|
|
||||||
|
private static final String PATH = "part1.txt";
|
||||||
|
private static final Pattern LATN_PATTERN = Pattern.compile("\\b\\p{IsLatin}+\\b",
|
||||||
|
Pattern.UNICODE_CHARACTER_CLASS);
|
||||||
|
private static final Pattern CYRL_PATTERN = Pattern.compile("\\b\\p{IsCyrillic}+\\b",
|
||||||
|
Pattern.UNICODE_CHARACTER_CLASS);
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
|
||||||
|
String fileContent = null;
|
||||||
|
try {
|
||||||
|
fileContent = new String(Files.readAllBytes(Paths.get(PATH)));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
scanner.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String result;
|
||||||
|
String input;
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
input = scanner.nextLine();
|
||||||
|
|
||||||
|
if (input.equalsIgnoreCase("stop")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.equals("Latn")) {
|
||||||
|
result = extractWords(fileContent, LATN_PATTERN);
|
||||||
|
} else if (input.equals("Cyrl")) {
|
||||||
|
result = extractWords(fileContent, CYRL_PATTERN);
|
||||||
|
} else {
|
||||||
|
result = "Incorrect input";
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf("%s: %s\n", input, result);
|
||||||
|
}
|
||||||
|
scanner.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractWords(String text, Pattern pattern) {
|
||||||
|
String words = "";
|
||||||
|
Matcher matcher = pattern.matcher(text);
|
||||||
|
|
||||||
|
while (matcher.find())
|
||||||
|
words += " " + matcher.group();
|
||||||
|
|
||||||
|
return words.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class Part2 {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String symin = "asdf\n" +
|
||||||
|
"asdf\n" +
|
||||||
|
"fdsa\n" +
|
||||||
|
"Stop\n";
|
||||||
|
|
||||||
|
InputStream stdin = System.in;
|
||||||
|
|
||||||
|
try {
|
||||||
|
System.setIn(new ByteArrayInputStream(symin.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
WordContainer.main(args);
|
||||||
|
} finally {
|
||||||
|
System.setIn(stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Part3 {
|
||||||
|
|
||||||
|
private static final String BASE_NAME = "resources";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
|
||||||
|
String input;
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
input = scanner.nextLine();
|
||||||
|
|
||||||
|
if (input.equalsIgnoreCase("stop")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = input.split(" ");
|
||||||
|
|
||||||
|
Locale locale = new Locale(parts[1]);
|
||||||
|
ResourceBundle bundle = ResourceBundle.getBundle(BASE_NAME, locale);
|
||||||
|
System.out.println(bundle.getString(parts[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class Part4 {
|
||||||
|
|
||||||
|
private static final Path INPUT_FILE = Paths.get("part4.txt");
|
||||||
|
private static final Path OUTPUT_FILE = Paths.get("part4_sorted.txt");
|
||||||
|
|
||||||
|
private static final int NUM_INTEGERS = 10;
|
||||||
|
private static final int MAX_VALUE = 50;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
List<Integer> nums;
|
||||||
|
|
||||||
|
if (!Files.exists(INPUT_FILE)) {
|
||||||
|
Random random = new Random();
|
||||||
|
|
||||||
|
nums = new ArrayList<Integer>(NUM_INTEGERS);
|
||||||
|
for (int i = 0; i < NUM_INTEGERS; i++) {
|
||||||
|
nums.add(random.nextInt(MAX_VALUE + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tryWriteNums(INPUT_FILE, nums)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nums = tryReadNums(INPUT_FILE);
|
||||||
|
if (nums == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf("input ==> %s\n", joinInts(nums));
|
||||||
|
|
||||||
|
nums = sortDedup(nums);
|
||||||
|
if (!tryWriteNums(OUTPUT_FILE, nums)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf("output ==> %s\n", joinInts(nums));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Integer> sortDedup(List<Integer> nums) {
|
||||||
|
for (int i = 0; i < nums.size() - 1; i++) {
|
||||||
|
for (int j = 0; j < nums.size() - i - 1; j++) {
|
||||||
|
int a = nums.get(j);
|
||||||
|
int b = nums.get(j + 1);
|
||||||
|
|
||||||
|
if (a > b) {
|
||||||
|
nums.set(j, b);
|
||||||
|
nums.set(j + 1, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> dedup = new ArrayList<Integer>();
|
||||||
|
for (int num : nums) {
|
||||||
|
if (dedup.indexOf(num) == -1)
|
||||||
|
dedup.add(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dedup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean tryWriteNums(Path path, List<Integer> nums) {
|
||||||
|
try {
|
||||||
|
String s = "";
|
||||||
|
for (int i = 0; i < nums.size(); i++) {
|
||||||
|
s += " " + nums.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.write(path, s.trim().getBytes(StandardCharsets.UTF_8));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Integer> tryReadNums(Path path) {
|
||||||
|
List<Integer> nums = new ArrayList<Integer>(NUM_INTEGERS);
|
||||||
|
try {
|
||||||
|
String content = Files.readString(path);
|
||||||
|
for (String s : content.trim().split(" ")) {
|
||||||
|
nums.add(Integer.parseInt(s));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String joinInts(Iterable<Integer> numbers) {
|
||||||
|
String result = "";
|
||||||
|
for (int num : numbers) {
|
||||||
|
result += " " + num;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
public class Part5 {
|
||||||
|
|
||||||
|
private static final String SEPARATOR = "~~~~~~~";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Tree<Integer> tree = new Tree<>();
|
||||||
|
|
||||||
|
System.out.println(tree.add(3));
|
||||||
|
System.out.println(tree.add(3));
|
||||||
|
|
||||||
|
System.out.println(SEPARATOR);
|
||||||
|
tree.add(new Integer[] { 1, 2, 6, 4, 7, 0, 5 });
|
||||||
|
tree.print();
|
||||||
|
|
||||||
|
System.out.println(SEPARATOR);
|
||||||
|
System.out.println(tree.remove(5));
|
||||||
|
System.out.println(tree.remove(5));
|
||||||
|
|
||||||
|
System.out.println(SEPARATOR);
|
||||||
|
tree.print();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
public class Tree<E extends Comparable<E>> {
|
||||||
|
private Node<E> root;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
public Tree() {
|
||||||
|
root = null;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean add(E element) {
|
||||||
|
if (element == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldSize = size;
|
||||||
|
root = addRecursive(root, element);
|
||||||
|
return size > oldSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node<E> addRecursive(Node<E> node, E element) {
|
||||||
|
if (node == null) {
|
||||||
|
size++;
|
||||||
|
return new Node<>(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
int comparison = element.compareTo(node.data);
|
||||||
|
|
||||||
|
if (comparison < 0) {
|
||||||
|
node.left = addRecursive(node.left, element);
|
||||||
|
} else if (comparison > 0) {
|
||||||
|
node.right = addRecursive(node.right, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(E[] elements) {
|
||||||
|
if (elements == null) {
|
||||||
|
throw new IllegalArgumentException("Array cannot be null");
|
||||||
|
}
|
||||||
|
for (E e : elements) {
|
||||||
|
if (e != null) {
|
||||||
|
add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(E element) {
|
||||||
|
if (element == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldSize = size;
|
||||||
|
root = removeRecursive(root, element);
|
||||||
|
return size < oldSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node<E> removeRecursive(Node<E> node, E element) {
|
||||||
|
if (node == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int comparison = element.compareTo(node.data);
|
||||||
|
|
||||||
|
if (comparison < 0) {
|
||||||
|
node.left = removeRecursive(node.left, element);
|
||||||
|
} else if (comparison > 0) {
|
||||||
|
node.right = removeRecursive(node.right, element);
|
||||||
|
} else {
|
||||||
|
size--;
|
||||||
|
|
||||||
|
if (node.left == null && node.right == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.left == null) {
|
||||||
|
return node.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.right == null) {
|
||||||
|
return node.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<E> successor = findMin(node.right);
|
||||||
|
node.data = successor.data;
|
||||||
|
node.right = removeRecursive(node.right, successor.data);
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node<E> findMin(Node<E> node) {
|
||||||
|
while (node.left != null) {
|
||||||
|
node = node.left;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(E element) {
|
||||||
|
if (element == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return containsRecursive(root, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsRecursive(Node<E> node, E element) {
|
||||||
|
if (node == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int comparison = element.compareTo(node.data);
|
||||||
|
|
||||||
|
if (comparison == 0) {
|
||||||
|
return true;
|
||||||
|
} else if (comparison < 0) {
|
||||||
|
return containsRecursive(node.left, element);
|
||||||
|
} else {
|
||||||
|
return containsRecursive(node.right, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
root = null;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void print() {
|
||||||
|
if (root == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String pad = " ".repeat(root.width());
|
||||||
|
|
||||||
|
if (root.left != null) {
|
||||||
|
root.left.print(pad, "┌", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf("%s%s\n", root.data, root.pointer());
|
||||||
|
|
||||||
|
if (root.right != null) {
|
||||||
|
root.right.print(pad, "└", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Node<E> {
|
||||||
|
E data;
|
||||||
|
Node<E> left;
|
||||||
|
Node<E> right;
|
||||||
|
|
||||||
|
Node(E data) {
|
||||||
|
this.data = data;
|
||||||
|
this.left = null;
|
||||||
|
this.right = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width() {
|
||||||
|
return data.toString().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
String pointer() {
|
||||||
|
if (right != null && left != null) {
|
||||||
|
return "┤";
|
||||||
|
} else if (right != null) {
|
||||||
|
return "┐";
|
||||||
|
} else if (left != null) {
|
||||||
|
return "┘";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void print(String prev, String handle, boolean nested) {
|
||||||
|
String pad = " ".repeat(width());
|
||||||
|
String ptr = pointer();
|
||||||
|
|
||||||
|
if (left != null) {
|
||||||
|
left.print(prev + (nested ? "│" : " ") + pad, "┌", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf("%s%s%s%s\n", prev, handle, data, ptr);
|
||||||
|
|
||||||
|
if (right != null) {
|
||||||
|
right.print(prev + (!nested ? "│" : " ") + pad, "└", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Word implements Comparable<Word> {
|
||||||
|
private String content;
|
||||||
|
private int frequency;
|
||||||
|
|
||||||
|
public Word(String content) {
|
||||||
|
this.content = content;
|
||||||
|
this.frequency = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFrequency() {
|
||||||
|
return frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementFrequency() {
|
||||||
|
this.frequency++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Word word = (Word) o;
|
||||||
|
|
||||||
|
return content.equals(word.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return content + " : " + frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Word other) {
|
||||||
|
int frequencyComparison = Integer.compare(this.frequency, other.frequency);
|
||||||
|
|
||||||
|
if (frequencyComparison != 0) {
|
||||||
|
return frequencyComparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.content.compareTo(other.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class WordContainer {
|
||||||
|
|
||||||
|
private static Pattern WORD_PATTERN = Pattern.compile("\\S+");
|
||||||
|
|
||||||
|
private ArrayList<Word> words = new ArrayList<>();
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
WordContainer container = new WordContainer();
|
||||||
|
container.readAndProcessInput();
|
||||||
|
container.printWords();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readAndProcessInput() {
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
|
||||||
|
String input;
|
||||||
|
outer: while (scanner.hasNextLine()) {
|
||||||
|
input = scanner.nextLine();
|
||||||
|
|
||||||
|
Matcher matcher = WORD_PATTERN.matcher(input);
|
||||||
|
while (matcher.find()) {
|
||||||
|
String wordContent = matcher.group();
|
||||||
|
|
||||||
|
if (wordContent.equalsIgnoreCase("stop"))
|
||||||
|
break outer;
|
||||||
|
|
||||||
|
addWord(wordContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scanner.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addWord(String content) {
|
||||||
|
Word newWord = new Word(content);
|
||||||
|
|
||||||
|
int i = words.indexOf(newWord);
|
||||||
|
if (i == -1) {
|
||||||
|
words.add(newWord);
|
||||||
|
} else {
|
||||||
|
words.get(i).incrementFrequency();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printWords() {
|
||||||
|
words.sort(null);
|
||||||
|
for (Word word : words)
|
||||||
|
System.out.println(word);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
table = table
|
||||||
|
apple = apple
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
table = \u0441\u0442\u0456\u043b
|
||||||
|
apple = \u044f\u0431\u043b\u0443\u043a\u043e
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
public class Part1Test {
|
||||||
|
|
||||||
|
private static final InputStream STD_IN = System.in;
|
||||||
|
|
||||||
|
private static final PrintStream STD_OUT = System.out;
|
||||||
|
|
||||||
|
private static final String ENCODING = "UTF-8";
|
||||||
|
|
||||||
|
private static final String EOL_CHAR = "~";
|
||||||
|
|
||||||
|
private static final String[] EMPTY_ARGS = {};
|
||||||
|
|
||||||
|
private static final String INPUT_FILE = "part1.txt";
|
||||||
|
|
||||||
|
private static final String EOL = System.lineSeparator();
|
||||||
|
|
||||||
|
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
private final PrintStream out = new PrintStream(baos);
|
||||||
|
|
||||||
|
private static String toMultiline(String line) {
|
||||||
|
return line.replaceAll(EOL_CHAR, EOL).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(resources = "part1.csv", delimiter = ';')
|
||||||
|
void part1(String data, String consoleInput, String expectedLine) throws Exception {
|
||||||
|
String expected = toMultiline(expectedLine);
|
||||||
|
String input = toMultiline(consoleInput);
|
||||||
|
byte[] buf = input.getBytes(ENCODING);
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
|
||||||
|
System.setOut(out);
|
||||||
|
System.setIn(bais);
|
||||||
|
String actual = "";
|
||||||
|
byte[] content = null;
|
||||||
|
try {
|
||||||
|
content = Utils.getContent(INPUT_FILE);
|
||||||
|
Utils.saveContent(INPUT_FILE, toMultiline(data).getBytes(ENCODING));
|
||||||
|
|
||||||
|
Part1.main(EMPTY_ARGS);
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
actual = baos.toString();
|
||||||
|
} finally {
|
||||||
|
System.setOut(STD_OUT);
|
||||||
|
System.setIn(STD_IN);
|
||||||
|
Utils.saveContent(INPUT_FILE, content);
|
||||||
|
}
|
||||||
|
assertEquals(expected.trim(), actual.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
public class Part2Test {
|
||||||
|
|
||||||
|
private static final InputStream STD_IN = System.in;
|
||||||
|
|
||||||
|
private static final PrintStream STD_OUT = System.out;
|
||||||
|
|
||||||
|
private static final String TEST_DATA_FILE_ENCODINE = "UTF-8";
|
||||||
|
|
||||||
|
private static final String EOL_CHAR = "~";
|
||||||
|
|
||||||
|
private static final String[] EMPTY_ARGS = {};
|
||||||
|
|
||||||
|
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
private final PrintStream out = new PrintStream(baos);
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(resources = "part2.csv", delimiter = ';')
|
||||||
|
void part2(String inputLine, String expectedLine) throws Exception {
|
||||||
|
String input = inputLine.replace(EOL_CHAR, System.lineSeparator());
|
||||||
|
String expected = expectedLine.replace(EOL_CHAR, System.lineSeparator());
|
||||||
|
byte[] buf = input.getBytes(TEST_DATA_FILE_ENCODINE);
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
|
||||||
|
System.setOut(out);
|
||||||
|
System.setIn(bais);
|
||||||
|
try {
|
||||||
|
WordContainer.main(EMPTY_ARGS);
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
String actual = baos.toString();
|
||||||
|
assertEquals(expected.trim(), actual.trim());
|
||||||
|
} finally {
|
||||||
|
System.setOut(STD_OUT);
|
||||||
|
System.setIn(STD_IN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
public class Part3Test {
|
||||||
|
|
||||||
|
private static final InputStream STD_IN = System.in;
|
||||||
|
|
||||||
|
private static final PrintStream STD_OUT = System.out;
|
||||||
|
|
||||||
|
private static final String TEST_DATA_FILE_ENCODINE = "UTF-8";
|
||||||
|
|
||||||
|
private static final String EOL_CHAR = "~";
|
||||||
|
|
||||||
|
private static final String[] EMPTY_ARGS = {};
|
||||||
|
|
||||||
|
private static final String EOL = System.lineSeparator();
|
||||||
|
|
||||||
|
private static final String BASE_NAME = "resources";
|
||||||
|
|
||||||
|
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
private final PrintStream out = new PrintStream(baos);
|
||||||
|
|
||||||
|
private static String toMultiline(String line) {
|
||||||
|
return line.replaceAll(EOL_CHAR, EOL).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String removeCR(String s) {
|
||||||
|
return s.replaceAll("\r?", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final URI classPathURI;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
classPathURI = Part3.class.getProtectionDomain().getCodeSource().getLocation().toURI();
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(resources = "part3.csv", delimiter = ';')
|
||||||
|
void part3(String defaultLocale, String localesString, String consoleInput, String expectedLine) throws Exception {
|
||||||
|
ResourceBundle.clearCache();
|
||||||
|
Locale.setDefault(new Locale(defaultLocale));
|
||||||
|
|
||||||
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Path.of(classPathURI))) {
|
||||||
|
for (Path p : stream) {
|
||||||
|
if (p.toString().endsWith(".properties")) {
|
||||||
|
Files.delete(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Path classPath = Paths.get(classPathURI);
|
||||||
|
List<String> locales = Arrays.asList(localesString.trim().split(" "));
|
||||||
|
String actual = "";
|
||||||
|
InputStream is = null;
|
||||||
|
try {
|
||||||
|
for (String locale : locales) {
|
||||||
|
String bundleName = BASE_NAME + '_' + locale + ".properties";
|
||||||
|
is = getClass().getClassLoader().getResourceAsStream(locale + ".props");
|
||||||
|
try (OutputStream out = new FileOutputStream(new File(classPath.toString(), bundleName))) {
|
||||||
|
is.transferTo(out);
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String input = toMultiline(consoleInput);
|
||||||
|
byte[] buf = input.getBytes(TEST_DATA_FILE_ENCODINE);
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
|
||||||
|
System.setOut(out);
|
||||||
|
System.setIn(bais);
|
||||||
|
|
||||||
|
Part3.main(EMPTY_ARGS);
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
actual = baos.toString();
|
||||||
|
} finally {
|
||||||
|
System.setOut(STD_OUT);
|
||||||
|
System.setIn(STD_IN);
|
||||||
|
}
|
||||||
|
assertEquals(removeCR(toMultiline(expectedLine)).trim(), removeCR(actual).trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
public class Part4Test {
|
||||||
|
|
||||||
|
private static final InputStream STD_IN = System.in;
|
||||||
|
|
||||||
|
private static final PrintStream STD_OUT = System.out;
|
||||||
|
|
||||||
|
private static final String EOL_CHAR = "~";
|
||||||
|
|
||||||
|
private static final String[] EMPTY_ARGS = {};
|
||||||
|
|
||||||
|
private static final String INPUT_FILE = "part4.txt";
|
||||||
|
|
||||||
|
private static final String EOL = System.lineSeparator();
|
||||||
|
|
||||||
|
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
private final PrintStream out = new PrintStream(baos);
|
||||||
|
|
||||||
|
private static String toMultiline(String line) {
|
||||||
|
return line.replaceAll(EOL_CHAR, EOL).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void tearDown() throws IOException {
|
||||||
|
Files.deleteIfExists(Path.of(INPUT_FILE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(resources = "part4.csv", delimiter = ';')
|
||||||
|
void part4(String inputData, String expected) throws Exception {
|
||||||
|
Files.writeString(Paths.get(INPUT_FILE), inputData);
|
||||||
|
System.setOut(out);
|
||||||
|
String actual = "";
|
||||||
|
try {
|
||||||
|
Part4.main(EMPTY_ARGS);
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
actual = baos.toString();
|
||||||
|
} finally {
|
||||||
|
System.setOut(STD_OUT);
|
||||||
|
System.setIn(STD_IN);
|
||||||
|
}
|
||||||
|
assertEquals(toMultiline(expected).trim(), actual.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvFileSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmytro Kolesnykov
|
||||||
|
*/
|
||||||
|
public class Part5Test {
|
||||||
|
|
||||||
|
private static final PrintStream STD_OUT = System.out;
|
||||||
|
|
||||||
|
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
private final PrintStream out = new PrintStream(baos);
|
||||||
|
|
||||||
|
private static String removeCR(String s) {
|
||||||
|
return s.replaceAll("\r?", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvFileSource(resources = "part5.csv", delimiter = ';', ignoreLeadingAndTrailingWhitespace = false)
|
||||||
|
void part5(String actionsData, String expectedTree) throws Exception {
|
||||||
|
String expected = expectedTree == null
|
||||||
|
? ""
|
||||||
|
: expectedTree.replaceAll("~", System.lineSeparator());
|
||||||
|
|
||||||
|
Tree<Integer> tree = new Tree<>();
|
||||||
|
for (String action : actionsData.split("\\s+")) {
|
||||||
|
String actionName = actionResolver(action.charAt(0));
|
||||||
|
Method actionMethod = Tree.class.getMethod(actionName, Comparable.class);
|
||||||
|
String actionValues = action.substring(action.indexOf('-') + 1);
|
||||||
|
for (String value : actionValues.split("-")) {
|
||||||
|
actionMethod.invoke(tree, new Object[] { Integer.parseInt(value) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String actual = null;
|
||||||
|
try {
|
||||||
|
System.setOut(out);
|
||||||
|
|
||||||
|
tree.print();
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
actual = baos.toString();
|
||||||
|
} finally {
|
||||||
|
System.setOut(STD_OUT);
|
||||||
|
}
|
||||||
|
expected = removeCR(expected).stripTrailing();
|
||||||
|
actual = removeCR(actual.replaceAll("(?m)\\s+$", "").stripTrailing());
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String actionResolver(char actionChar) {
|
||||||
|
switch (actionChar) {
|
||||||
|
case 'a':
|
||||||
|
return "add";
|
||||||
|
case 'r':
|
||||||
|
return "remove";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package ua.nure.jfn.task4;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class Utils {
|
||||||
|
|
||||||
|
public static byte[] getContent(String fileName) throws IOException {
|
||||||
|
return Files.readAllBytes(Paths.get(fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveContent(String fileName, byte[] content) throws IOException {
|
||||||
|
if (content != null) {
|
||||||
|
Files.write(Paths.get(fileName), content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
table = table
|
||||||
|
apple = apple
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
table = \u10db\u10d0\u10d2\u10d8\u10d3\u10d0
|
||||||
|
apple = \u10d5\u10d0\u10e8\u10da\u10d8
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for f in *-data.txt; do
|
||||||
|
sed -rne '
|
||||||
|
/^#/d
|
||||||
|
/^\s*$/d
|
||||||
|
/^~~~/ {
|
||||||
|
g
|
||||||
|
s/\n/~/g
|
||||||
|
s/$/;/
|
||||||
|
h
|
||||||
|
b
|
||||||
|
}
|
||||||
|
/^===/ {
|
||||||
|
g
|
||||||
|
s/\n/~/g
|
||||||
|
s/;~/;/g
|
||||||
|
s/^~//
|
||||||
|
p
|
||||||
|
s/^.*$//
|
||||||
|
h
|
||||||
|
b
|
||||||
|
}
|
||||||
|
H
|
||||||
|
' $f>${f/-data.txt/.csv}
|
||||||
|
done
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#1
|
||||||
|
Is there anybody
|
||||||
|
going to listen to my story
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
asdf
|
||||||
|
Latn
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
asdf: Incorrect input
|
||||||
|
Latn: Is there anybody going to listen to my story
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#2
|
||||||
|
Is there anybody going to listen
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
asdf
|
||||||
|
Latn
|
||||||
|
stop
|
||||||
|
Latn
|
||||||
|
asdf
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
asdf: Incorrect input
|
||||||
|
Latn: Is there anybody going to listen
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#3
|
||||||
|
Is there listen
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
asdf
|
||||||
|
Latn
|
||||||
|
Stop
|
||||||
|
Latn
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
asdf: Incorrect input
|
||||||
|
Latn: Is there listen
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#4
|
||||||
|
Incorrect input
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
a
|
||||||
|
Latn
|
||||||
|
b
|
||||||
|
stop
|
||||||
|
a
|
||||||
|
Latn
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
a: Incorrect input
|
||||||
|
Latn: Incorrect input
|
||||||
|
b: Incorrect input
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#5
|
||||||
|
Сонце гріє лице,
|
||||||
|
Радує це!
|
||||||
|
Ooh, baby, I love you
|
||||||
|
What more
|
||||||
|
can I say
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
latn
|
||||||
|
Latn
|
||||||
|
cyrl
|
||||||
|
Cyrl
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
latn: Incorrect input
|
||||||
|
Latn: Ooh baby I love you What more can I say
|
||||||
|
cyrl: Incorrect input
|
||||||
|
Cyrl: Сонце гріє лице Радує це
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Is there anybody ~going to listen to my story;asdf~Latn;asdf: Incorrect input~Latn: Is there anybody going to listen to my story
|
||||||
|
Is there anybody going to listen;asdf~Latn~stop~Latn~asdf;asdf: Incorrect input~Latn: Is there anybody going to listen
|
||||||
|
Is there listen;asdf~Latn~Stop~Latn;asdf: Incorrect input~Latn: Is there listen
|
||||||
|
Incorrect input;a~Latn~b~stop~a~Latn;a: Incorrect input~Latn: Incorrect input~b: Incorrect input
|
||||||
|
Сонце гріє лице,~ Радує це!~Ooh, baby, I love you~ What more ~can I say;latn~Latn~cyrl~Cyrl;latn: Incorrect input~Latn: Ooh baby I love you What more can I say~cyrl: Incorrect input~Cyrl: Сонце гріє лице Радує це
|
||||||
|
@@ -0,0 +1,44 @@
|
|||||||
|
#1
|
||||||
|
a~b
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
a : 1
|
||||||
|
b : 1
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#2
|
||||||
|
a b a d~b c stop b
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
c : 1
|
||||||
|
d : 1
|
||||||
|
a : 2
|
||||||
|
b : 2
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#3
|
||||||
|
a b a stop d~b c stop b
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
b : 1
|
||||||
|
a : 2
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#4
|
||||||
|
ab ab abd
|
||||||
|
b abd
|
||||||
|
stop
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
b : 1
|
||||||
|
ab : 2
|
||||||
|
abd : 2
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#5
|
||||||
|
a a a b b stop c c
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
b : 2
|
||||||
|
a : 3
|
||||||
|
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
a~b;a : 1~b : 1
|
||||||
|
a b a d~b c stop b;c : 1~d : 1~a : 2~b : 2
|
||||||
|
a b a stop d~b c stop b;b : 1~a : 2
|
||||||
|
ab ab abd~b abd~stop;b : 1~ab : 2~abd : 2
|
||||||
|
a a a b b stop c c;b : 2~a : 3
|
||||||
|
@@ -0,0 +1,101 @@
|
|||||||
|
#1
|
||||||
|
en
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
en uk
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table en
|
||||||
|
table ka
|
||||||
|
table uk
|
||||||
|
apple en
|
||||||
|
apple uk
|
||||||
|
stop
|
||||||
|
ka
|
||||||
|
apple
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table
|
||||||
|
table
|
||||||
|
стіл
|
||||||
|
apple
|
||||||
|
яблуко
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#2
|
||||||
|
uk
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
en uk
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table en
|
||||||
|
table ka
|
||||||
|
table uk
|
||||||
|
apple en
|
||||||
|
apple uk
|
||||||
|
stop
|
||||||
|
ka
|
||||||
|
apple
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table
|
||||||
|
стіл
|
||||||
|
стіл
|
||||||
|
apple
|
||||||
|
яблуко
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#3
|
||||||
|
en
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
en uk ka
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table en
|
||||||
|
table ka
|
||||||
|
table uk
|
||||||
|
apple en
|
||||||
|
apple uk
|
||||||
|
stop
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table
|
||||||
|
მაგიდა
|
||||||
|
стіл
|
||||||
|
apple
|
||||||
|
яблуко
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#4
|
||||||
|
ka
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
en ka uk
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table en
|
||||||
|
table ka
|
||||||
|
table uk
|
||||||
|
apple en
|
||||||
|
apple uk
|
||||||
|
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table
|
||||||
|
მაგიდა
|
||||||
|
стіл
|
||||||
|
apple
|
||||||
|
яблуко
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#5
|
||||||
|
uk
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
en uk
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table en
|
||||||
|
table en
|
||||||
|
apple uk
|
||||||
|
apple uk
|
||||||
|
stop
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
table
|
||||||
|
table
|
||||||
|
яблуко
|
||||||
|
яблуко
|
||||||
|
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
en;en uk;table en~table ka~table uk~apple en~apple uk~stop~ka~apple;table~table~стіл~apple~яблуко
|
||||||
|
uk;en uk;table en~table ka~table uk~apple en~apple uk~stop~ka~apple;table~стіл~стіл~apple~яблуко
|
||||||
|
en;en uk ka;table en~table ka~table uk~apple en~apple uk~stop;table~მაგიდა~стіл~apple~яблуко
|
||||||
|
ka;en ka uk;table en~table ka~table uk~apple en~apple uk;table~მაგიდა~стіл~apple~яблуко
|
||||||
|
uk;en uk;table en~table en~apple uk~apple uk~stop;table~table~яблуко~яблуко
|
||||||
|
@@ -0,0 +1,39 @@
|
|||||||
|
#1
|
||||||
|
1 2 3 4 5 4 3 2 1
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
input ==> 1 2 3 4 5 4 3 2 1
|
||||||
|
output ==> 1 2 3 4 5
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#2
|
||||||
|
4 3 1 3 4 1 3 4 1 3 4
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
input ==> 4 3 1 3 4 1 3 4 1 3 4
|
||||||
|
output ==> 1 3 4
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#3
|
||||||
|
4 9 8 2 9 4 8 9 1 9
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
input ==> 4 9 8 2 9 4 8 9 1 9
|
||||||
|
output ==> 1 2 4 8 9
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#4
|
||||||
|
11 12 11 18 12 11 18 17
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
input ==> 11 12 11 18 12 11 18 17
|
||||||
|
output ==> 11 12 17 18
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#5
|
||||||
|
0
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
input ==> 0
|
||||||
|
output ==> 0
|
||||||
|
|
||||||
|
====================================
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
1 2 3 4 5 4 3 2 1;input ==> 1 2 3 4 5 4 3 2 1~output ==> 1 2 3 4 5
|
||||||
|
4 3 1 3 4 1 3 4 1 3 4;input ==> 4 3 1 3 4 1 3 4 1 3 4~output ==> 1 3 4
|
||||||
|
4 9 8 2 9 4 8 9 1 9;input ==> 4 9 8 2 9 4 8 9 1 9~output ==> 1 2 4 8 9
|
||||||
|
11 12 11 18 12 11 18 17;input ==> 11 12 11 18 12 11 18 17~output ==> 11 12 17 18
|
||||||
|
0;input ==> 0~output ==> 0
|
||||||
|
@@ -0,0 +1,199 @@
|
|||||||
|
#1
|
||||||
|
a-3
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
3
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#2
|
||||||
|
a-3-1-2-6-4-7-0-5
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
┌1┤
|
||||||
|
│ └2
|
||||||
|
3┤
|
||||||
|
│ ┌4┐
|
||||||
|
│ │ └5
|
||||||
|
└6┤
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#3
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
┌1┤
|
||||||
|
│ └2
|
||||||
|
4┤
|
||||||
|
│ ┌5
|
||||||
|
└6┤
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#4
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-5
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
┌1┤
|
||||||
|
│ └2
|
||||||
|
4┤
|
||||||
|
└6┐
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#5
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-5-4
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
┌1┤
|
||||||
|
│ └2
|
||||||
|
6┤
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#6
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-5-4-1
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
┌2┘
|
||||||
|
6┤
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#7
|
||||||
|
a-3-1-2-6-4-7-0 r-3-5-4-1
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
┌2┘
|
||||||
|
6┤
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#8
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-5-4-1-6-7-2-0-0 a-3-2
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌2
|
||||||
|
3┘
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#9
|
||||||
|
a-3-3-2-2-1-1 r-2-1
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
3
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#10
|
||||||
|
a-3-3-1-1
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌1
|
||||||
|
3┘
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#11
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
┌1┤
|
||||||
|
│ └2
|
||||||
|
7┘
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#12
|
||||||
|
a-3-12-116-14-7-0-5 r-3
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
5┤
|
||||||
|
│ ┌7
|
||||||
|
└12┤
|
||||||
|
│ ┌14
|
||||||
|
└116┘
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#13
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
┌1┤
|
||||||
|
│ └2
|
||||||
|
5┤
|
||||||
|
└6┐
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#14
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6-7
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
1┤
|
||||||
|
└2
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#15
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6-7-1
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌0
|
||||||
|
2┘
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#16
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6-7-1-2
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
0
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#17
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6-7-1-2-0
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#18
|
||||||
|
a-3-1-2-6-4-7 r-3 a-3
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌1┐
|
||||||
|
│ └2┐
|
||||||
|
│ └3
|
||||||
|
4┤
|
||||||
|
└6┐
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#19
|
||||||
|
a-3-1-2-6-4-7 r-3 a-3 r-2 a-2
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌1┐
|
||||||
|
│ │ ┌2
|
||||||
|
│ └3┘
|
||||||
|
4┤
|
||||||
|
└6┐
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
|
#20
|
||||||
|
a-3-1-2-6-4-7 r-3 a-3 r-2 a-2 r-6
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
┌1┐
|
||||||
|
│ │ ┌2
|
||||||
|
│ └3┘
|
||||||
|
4┤
|
||||||
|
└7
|
||||||
|
|
||||||
|
====================================
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
a-3;3
|
||||||
|
a-3-1-2-6-4-7-0-5; ┌0~ ┌1┤~ │ └2~3┤~ │ ┌4┐~ │ │ └5~ └6┤~ └7
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3; ┌0~ ┌1┤~ │ └2~4┤~ │ ┌5~ └6┤~ └7
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-5; ┌0~ ┌1┤~ │ └2~4┤~ └6┐~ └7
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-5-4; ┌0~ ┌1┤~ │ └2~6┤~ └7
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-5-4-1; ┌0~ ┌2┘~6┤~ └7
|
||||||
|
a-3-1-2-6-4-7-0 r-3-5-4-1; ┌0~ ┌2┘~6┤~ └7
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-5-4-1-6-7-2-0-0 a-3-2; ┌2~3┘
|
||||||
|
a-3-3-2-2-1-1 r-2-1;3
|
||||||
|
a-3-3-1-1; ┌1~3┘
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6; ┌0~ ┌1┤~ │ └2~7┘
|
||||||
|
a-3-12-116-14-7-0-5 r-3; ┌0~5┤~ │ ┌7~ └12┤~ │ ┌14~ └116┘
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4; ┌0~ ┌1┤~ │ └2~5┤~ └6┐~ └7
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6-7; ┌0~1┤~ └2
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6-7-1; ┌0~2┘
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6-7-1-2;0
|
||||||
|
a-3-1-2-6-4-7-0-5 r-3-4-5-6-7-1-2-0;
|
||||||
|
a-3-1-2-6-4-7 r-3 a-3; ┌1┐~ │ └2┐~ │ └3~4┤~ └6┐~ └7
|
||||||
|
a-3-1-2-6-4-7 r-3 a-3 r-2 a-2; ┌1┐~ │ │ ┌2~ │ └3┘~4┤~ └6┐~ └7
|
||||||
|
a-3-1-2-6-4-7 r-3 a-3 r-2 a-2 r-6; ┌1┐~ │ │ ┌2~ │ └3┘~4┤~ └7
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
table = \u0441\u0442\u0456\u043b
|
||||||
|
apple = \u044f\u0431\u043b\u0443\u043a\u043e
|
||||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
> [!NOTE]
|
||||||
|
> Викладач: Сокорчук І. П.
|
||||||
|
>
|
||||||
|
> Оцінка: 100
|
||||||
+469
@@ -0,0 +1,469 @@
|
|||||||
|
#!/usr/bin/php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class DbException extends Exception {}
|
||||||
|
class AppException extends Exception {}
|
||||||
|
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
case Hello;
|
||||||
|
case Menu;
|
||||||
|
case Items;
|
||||||
|
case Checkout;
|
||||||
|
case Settins;
|
||||||
|
case Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DB
|
||||||
|
{
|
||||||
|
private $pdo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes database
|
||||||
|
*
|
||||||
|
* @param string $db_path
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function __construct($db_path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->pdo = new PDO("sqlite:" . $db_path);
|
||||||
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Connection to DB failed.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
name TEXT,
|
||||||
|
age TEXT
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
if ($this->pdo->query("SELECT COUNT(*) FROM settings;")->fetchColumn() == 0) {
|
||||||
|
$this->pdo->exec("INSERT INTO settings (name, age) VALUES ('user', 0);");
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising settings table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS items (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
price REAL NOT NULL
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
if ($this->pdo->query("SELECT COUNT(id) FROM items;")->fetchColumn() == 0) {
|
||||||
|
$this->pdo->exec("
|
||||||
|
INSERT INTO items (name, price) VALUES ('Молоко пастеризоване', 12);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Хліб чорний', 9);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сир білий', 21);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сметана 20%', 25);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Кефір 1%', 19);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Вода газована', 18);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Печиво \"Весна\"', 14);
|
||||||
|
");
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS cart (
|
||||||
|
id INTEGER NOT NULL UNIQUE,
|
||||||
|
count INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(id) REFERENCES item(id)
|
||||||
|
);
|
||||||
|
");
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising cart table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates user information in the database
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param int $age
|
||||||
|
* @return void
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function update_user($name, $age): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("UPDATE settings SET name = :name, age = :age;");
|
||||||
|
|
||||||
|
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':age', $age, PDO::PARAM_INT);
|
||||||
|
|
||||||
|
$stmt->execute();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error updating user info.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items from the database.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_items(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query("SELECT id, name, price FROM items ORDER BY id;");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error retrieving data from the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items in the cart from the database without price info.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart_no_price(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT name, count FROM cart
|
||||||
|
INNER JOIN items ON cart.id = items.id
|
||||||
|
ORDER BY cart.id;"
|
||||||
|
);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error inserting init data in the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items in the cart from the database.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT
|
||||||
|
cart.id, name, price, count, price*count as total_price
|
||||||
|
FROM cart
|
||||||
|
INNER JOIN items ON cart.id = items.id
|
||||||
|
ORDER BY cart.id;"
|
||||||
|
);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error inserting init data in the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add item to the cart
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @param int $count
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function add_to_cart($id, $count): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
"INSERT INTO cart
|
||||||
|
(id, count)
|
||||||
|
VALUES
|
||||||
|
(:id, :count)
|
||||||
|
ON CONFLICT(id)
|
||||||
|
DO UPDATE SET
|
||||||
|
count = :count
|
||||||
|
WHERE id = :id;"
|
||||||
|
);
|
||||||
|
return $stmt->execute(['id' => $id, 'count' => $count]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error adding item to the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cart
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function remove_from_cart($id): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("DELETE FROM cart WHERE id = :id");
|
||||||
|
return $stmt->execute(['id' => $id]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error removimg item from the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class App
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $state;
|
||||||
|
|
||||||
|
private $menu_ops = <<<'END'
|
||||||
|
1 Вибрати товари
|
||||||
|
2 Отримати підсумковий рахунок
|
||||||
|
3 Налаштувати свій профіль
|
||||||
|
0 Вийти з програми
|
||||||
|
END;
|
||||||
|
private $hello = <<<'END'
|
||||||
|
################################
|
||||||
|
# ПРОДОВОЛЬЧИЙ МАГАЗИН "ВЕСНА" #
|
||||||
|
################################
|
||||||
|
END;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $db_path
|
||||||
|
*/
|
||||||
|
public function __construct($db_path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->db = new DB($db_path);
|
||||||
|
$this->state = State::Hello;
|
||||||
|
} catch (DbException $e) {
|
||||||
|
throw new AppException("Error initializing app.\nCaused by: " . $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function poll(): void
|
||||||
|
{
|
||||||
|
while ($this->state != State::Exit) {
|
||||||
|
switch ($this->state) {
|
||||||
|
case State::Hello:
|
||||||
|
$this->hello();
|
||||||
|
break;
|
||||||
|
case State::Menu:
|
||||||
|
$this->menu();
|
||||||
|
break;
|
||||||
|
case State::Items:
|
||||||
|
$this->items();
|
||||||
|
break;
|
||||||
|
case State::Checkout:
|
||||||
|
$this->checkout();
|
||||||
|
break;
|
||||||
|
case State::Settins:
|
||||||
|
$this->settings();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function menu(): void
|
||||||
|
{
|
||||||
|
echo "\n";
|
||||||
|
echo "$this->menu_ops\n";
|
||||||
|
|
||||||
|
$op = readline('Введіть команду: ');
|
||||||
|
switch ($op) {
|
||||||
|
case '1':
|
||||||
|
$this->state = State::Items;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
$this->state = State::Checkout;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
$this->state = State::Settins;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
$this->state = State::Exit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo "ПОМИЛКА! Введіть правильну команду\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
private function hello(): void
|
||||||
|
{
|
||||||
|
echo "$this->hello\n";
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function items(): void
|
||||||
|
{
|
||||||
|
$items = $this->db->get_items();
|
||||||
|
array_unshift($items, ['id' => "№", 'name' => "НАЗВА", 'price' => "ЦІНА"]);
|
||||||
|
array_push($items, ['id' => " ", 'name' => "-----------", 'price' => ""]);
|
||||||
|
array_push($items, ['id' => "0", 'name' => "ПОВЕРНУТИСЯ", 'price' => ""]);
|
||||||
|
$columns = $this->count_columns($items);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$this->print_lits($items, $columns);
|
||||||
|
|
||||||
|
$id = readline("Виберіть товар: ");
|
||||||
|
|
||||||
|
if ($id == '0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
$selected = null;
|
||||||
|
foreach ($items as $item) {
|
||||||
|
if ($item['id'] === (int)$id) $selected = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($selected == null) {
|
||||||
|
echo "ПОМИЛКА! ВКАЗАНО НЕПРАВИЛЬНИЙ НОМЕР ТОВАРУ\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Вибрано: {$selected['name']}\n";
|
||||||
|
|
||||||
|
$count = (int)readline("Введіть кількість, штук: ");
|
||||||
|
|
||||||
|
if ($count > 100) {
|
||||||
|
echo "ПОМИЛКА! Не можна додати більше 100 одиниць товару в кошик\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($count < 0) {
|
||||||
|
echo "ПОМИЛКА! Кількість не може бути від'ємною\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($count == 0) {
|
||||||
|
echo "ВИДАЛЯЮ З КОШИКА\n";
|
||||||
|
$this->db->remove_from_cart($id);
|
||||||
|
} else {
|
||||||
|
$this->db->add_to_cart($id, $count);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart = $this->db->get_cart_no_price();
|
||||||
|
if (count($cart) == 0) {
|
||||||
|
echo "КОШИК ПОРОЖНІЙ\n";
|
||||||
|
} else {
|
||||||
|
echo "\nУ КОШИКУ:\n";
|
||||||
|
array_unshift($cart, ['name' => "НАЗВА", 'count' => "КІЛЬКІСТЬ"]);
|
||||||
|
$cart_columns = $this->count_columns($cart);
|
||||||
|
$this->print_lits($cart, $cart_columns);
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function checkout(): void
|
||||||
|
{
|
||||||
|
$cart = $this->db->get_cart();
|
||||||
|
if (count($cart) == 0) {
|
||||||
|
echo "КОШИК ПОРОЖНІЙ\n";
|
||||||
|
$this->state = State::Menu;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
echo "У КОШИКУ:\n";
|
||||||
|
array_unshift($cart, ['id' => "№", 'name' => "НАЗВА", 'price' => "ЦІНА", 'count' => "КІЛЬКІСТЬ", 'total_price' => "ВАРТІСТЬ"]);
|
||||||
|
$cart_columns = $this->count_columns($cart);
|
||||||
|
$this->print_lits($cart, $cart_columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
$total_price = array_reduce($cart, function ($carry, $item) {
|
||||||
|
return $carry + (int)$item['total_price'];
|
||||||
|
}, 0);
|
||||||
|
echo "РАЗОМ ДО СПЛАТИ: {$total_price}\n";
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function settings(): void
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
$name = readline("Ваше ім'я: ");
|
||||||
|
if ($name !== "" && preg_match("/[a-zA-Z]+/", $name))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$age = readline("Ваш вік: ");
|
||||||
|
|
||||||
|
if (!filter_var($age, FILTER_VALIDATE_INT)) {
|
||||||
|
echo "ПОМИЛКА! Вік користувача потрібно вказати числом\n\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($age < 7 || $age > 150) {
|
||||||
|
echo "ПОМИЛКА! Користувач повинен мати вік від 7 та до 150 років\n\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
$this->db->update_user($name, $age);
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<array> $items
|
||||||
|
* @return array<int>
|
||||||
|
*/
|
||||||
|
private function count_columns($items): array
|
||||||
|
{
|
||||||
|
$columns = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
foreach ($item as $field => $value) {
|
||||||
|
if (!key_exists($field, $columns))
|
||||||
|
$columns[$field] = mb_strlen($value);
|
||||||
|
else
|
||||||
|
$columns[$field] = max(mb_strlen($value), $columns[$field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param array $element
|
||||||
|
* @param array<int> $columns
|
||||||
|
*/
|
||||||
|
private function pad_row($element, $columns): string
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
foreach ($element as $field => $value)
|
||||||
|
$result[] = mb_str_pad($value, $columns[$field], ' ', STR_PAD_RIGHT);
|
||||||
|
|
||||||
|
return implode(" ", $result);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param array<array> $items
|
||||||
|
* @param array<int> $columns
|
||||||
|
*/
|
||||||
|
private function print_lits($items, $columns): void
|
||||||
|
{
|
||||||
|
foreach ($items as $item)
|
||||||
|
echo $this->pad_row($item, $columns) . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app = new App("db.sqlite");
|
||||||
|
} catch (AppException $e) {
|
||||||
|
echo $e;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$app->poll();
|
||||||
Binary file not shown.
@@ -0,0 +1,681 @@
|
|||||||
|
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
|
||||||
|
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Кафедра Програмної інженерії
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Звіт
|
||||||
|
з лабораторної роботи №2
|
||||||
|
з дисципліни: «Скриптові мови програмування»
|
||||||
|
з теми: «Продовольчий магазин «Весна»»
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Виконав: Перевірив:
|
||||||
|
ст. гр. ПЗПІ-23-2 Старший викладач кафедри ПІ
|
||||||
|
Ситник Є. С. Сокорчук І. П.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Харків – 2025
|
||||||
|
2
|
||||||
|
2 ПРОДОВОЛЬЧИЙ МАГАЗИН «ВЕСНА»
|
||||||
|
2.1 Історія змін
|
||||||
|
|
||||||
|
№ Дата Версія звіту Опис змін та виправлень
|
||||||
|
1 03.03.2025 0.1 Створено звіт
|
||||||
|
|
||||||
|
2.2 Мета роботи
|
||||||
|
|
||||||
|
Лабораторна робота полягає у розробці консольного застосунку
|
||||||
|
«Продовольчий магазин «Весна»» засобами мови програмування «PHP».
|
||||||
|
|
||||||
|
2.3 Хід роботи
|
||||||
|
2.3.1 Архітектура програми
|
||||||
|
|
||||||
|
Для реалізації консольного застосунку було обрано об’єктно-орієнтований
|
||||||
|
підхід з використанням наступних компонентів:
|
||||||
|
|
||||||
|
2.3.1.1 Класи та їх призначення:
|
||||||
|
|
||||||
|
– DB – клас для роботи з базою даних «SQLite», що забезпечує збереження
|
||||||
|
даних про товари, кошик користувача та налаштування профілю;
|
||||||
|
– App – основний клас застосунку, що реалізує логіку роботи програми та
|
||||||
|
взаємодію з користувачем;
|
||||||
|
– DbException та AppException – класи винятків для обробки помилок.
|
||||||
|
|
||||||
|
2.3.1.2 Enum State:
|
||||||
|
|
||||||
|
Для управління станами програми використовується перелічення «State» з
|
||||||
|
наступними значеннями:
|
||||||
|
– Hello – привітальний екран;
|
||||||
|
– Menu – головне меню;
|
||||||
|
– Items – вибір товарів;
|
||||||
|
– Checkout – формування рахунку;
|
||||||
|
– Settings – налаштування профілю;
|
||||||
|
– Exit – вихід з програми.
|
||||||
|
3
|
||||||
|
2.3.2 Структура бази даних
|
||||||
|
|
||||||
|
Програма використовує базу даних SQLite з трьома таблицями:
|
||||||
|
– settings – зберігає інформацію про користувача (ім’я та вік);
|
||||||
|
– items – містить каталог товарів з їх назвами та цінами;
|
||||||
|
– cart – зберігає товари, додані до кошика, з їх кількістю.
|
||||||
|
|
||||||
|
2.3.3 Основний функціонал
|
||||||
|
|
||||||
|
2.3.3.1 Головне меню:
|
||||||
|
|
||||||
|
Програма відображає заголовок магазину та пропонує користувачу чотири
|
||||||
|
основні опції:
|
||||||
|
– вибір товарів для покупки;
|
||||||
|
– перегляд підсумкового рахунку;
|
||||||
|
– налаштування профілю користувача;
|
||||||
|
– вихід з програми.
|
||||||
|
|
||||||
|
2.3.3.2 Вибір товарів:
|
||||||
|
|
||||||
|
При виборі першого пункту меню користувач потрапляє в режим покупки
|
||||||
|
товарів, де:
|
||||||
|
– відображається список доступних товарів з цінами;
|
||||||
|
– можна вибрати товар за номером та вказати кількість;
|
||||||
|
– товари додаються до кошика;
|
||||||
|
– при введенні кількості «0» товар видаляється з кошика;
|
||||||
|
– реалізована валідація введених даних.
|
||||||
|
|
||||||
|
2.3.3.3 Підсумковий рахунок:
|
||||||
|
|
||||||
|
Другий пункт меню формує детальний рахунок з інформацією про:
|
||||||
|
– номер, назву та ціну кожного товару;
|
||||||
|
– кількість товару в кошику;
|
||||||
|
– загальну вартість кожного товару;
|
||||||
|
– підсумкову суму до сплати.
|
||||||
|
4
|
||||||
|
2.3.3.4 Налаштування профілю:
|
||||||
|
|
||||||
|
Третій пункт дозволяє користувачу вказати своє ім’я та вік з валідацією:
|
||||||
|
– ім’я повинно містити хоча б одну літеру;
|
||||||
|
– вік повинен бути в діапазоні від 7 до 150 років.
|
||||||
|
|
||||||
|
2.3.4 Технічні деталі
|
||||||
|
|
||||||
|
Програма написана з дотриманням сучасних стандартів «PHP»:
|
||||||
|
– використання строгої типізації;
|
||||||
|
– документування методів за допомогою «PHPDoc»;
|
||||||
|
– дотримання принципів «SOLID»;
|
||||||
|
– розділення логіки на окремі методи для кращої читабельності.
|
||||||
|
|
||||||
|
2.4 Висновки
|
||||||
|
|
||||||
|
Під час даної лабораторної роботи я вивчив основні принципи роботи з «PHP»
|
||||||
|
для написання консольних програм. Зокрема, було освоєно:
|
||||||
|
– створення об’єктно-орієнтованих консольних застосунків на «PHP»;
|
||||||
|
– роботу з базою даних «SQLite» через «PDO»;
|
||||||
|
– обробку користувацького вводу та валідацію даних;
|
||||||
|
– використання перелічень («enum») для управління станами програми;
|
||||||
|
– реалізацію системи обробки винятків;
|
||||||
|
– форматування виводу в консолі для створення зручного інтерфейсу.
|
||||||
|
Програмне забезпечення, реалізоване протягом даної лабораторної роботи,
|
||||||
|
повністю відповідає поставленим вимогам та забезпечує всі необхідні функції для
|
||||||
|
роботи з продовольчим магазином. Застосунок має зручний інтерфейс, надійну
|
||||||
|
систему валідації даних та ефективно працює з базою даних для збереження
|
||||||
|
інформації про товари та користувача.
|
||||||
|
5
|
||||||
|
ДОДАТОК А
|
||||||
|
Відеозапис
|
||||||
|
|
||||||
|
Відеозапис презентації результатів лабораторної роботи:
|
||||||
|
https://youtu.be/CMGeL1OdyvI
|
||||||
|
|
||||||
|
Хронологічний опис відеозапису:
|
||||||
|
00:00 – Вступ та опис завдання
|
||||||
|
00:19 – Архітектура програми
|
||||||
|
00:39 – Робота з базою даних
|
||||||
|
01:43 – Реалізація станів програми
|
||||||
|
02:17 – Формування списку товарів
|
||||||
|
03:13 – Додавання товарів до кошика
|
||||||
|
04:15 – Робота з кошиком
|
||||||
|
05:10 – Налаштування профілю користувача
|
||||||
|
05:38 – Демонстрація роботи програми
|
||||||
|
6
|
||||||
|
ДОДАТОК Б
|
||||||
|
Програмний код
|
||||||
|
|
||||||
|
Б.1 Скрипт з реалізацією програми
|
||||||
|
|
||||||
|
GitHub репозиторій:
|
||||||
|
https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab2/smp-pzpi-23-2-sytnyk-yehor-lab2/smp-pzpi-23-2-sytnyk-yehor-lab2-code
|
||||||
|
|
||||||
|
1 #!/usr/bin/php
|
||||||
|
2
|
||||||
|
3 <?php
|
||||||
|
4
|
||||||
|
5 class DbException extends Exception {}
|
||||||
|
6 class AppException extends Exception {}
|
||||||
|
7
|
||||||
|
8 enum State
|
||||||
|
9 {
|
||||||
|
10 case Hello;
|
||||||
|
11 case Menu;
|
||||||
|
12 case Items;
|
||||||
|
13 case Checkout;
|
||||||
|
14 case Settins;
|
||||||
|
15 case Exit;
|
||||||
|
16 }
|
||||||
|
17
|
||||||
|
18 class DB
|
||||||
|
19 {
|
||||||
|
20 private $pdo;
|
||||||
|
21
|
||||||
|
22 /**
|
||||||
|
23 * Initializes database
|
||||||
|
24 *
|
||||||
|
25 * @param string $db_path
|
||||||
|
26 * @throws DbException If there's a database error.
|
||||||
|
27 */
|
||||||
|
28 public function __construct($db_path)
|
||||||
|
29 {
|
||||||
|
30 try {
|
||||||
|
31 $this->pdo = new PDO("sqlite:" . $db_path);
|
||||||
|
32 $this->pdo->setAttribute(PDO::ATTR_ERRMODE,
|
||||||
|
PDO::ERRMODE_EXCEPTION);
|
||||||
|
33 } catch (PDOException $e) {
|
||||||
|
34 throw new DbException("Connection to DB failed.\nCaused
|
||||||
|
by: " . $e->getMessage());
|
||||||
|
35 }
|
||||||
|
36
|
||||||
|
37 try {
|
||||||
|
38 $this->pdo->exec("
|
||||||
|
39 CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
40 name TEXT,
|
||||||
|
41 age TEXT
|
||||||
|
7
|
||||||
|
|
||||||
|
42 );
|
||||||
|
43 ");
|
||||||
|
44
|
||||||
|
45 if ($this->pdo->query("SELECT COUNT(*) FROM settings;")-
|
||||||
|
>fetchColumn() == 0) {
|
||||||
|
46 $this->pdo->exec("INSERT INTO settings (name, age)
|
||||||
|
VALUES ('user', 0);");
|
||||||
|
47 }
|
||||||
|
48 } catch (PDOException $e) {
|
||||||
|
49 throw new DbException("Error initialising settings table.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
50 }
|
||||||
|
51
|
||||||
|
52 try {
|
||||||
|
53 $this->pdo->exec("
|
||||||
|
54 CREATE TABLE IF NOT EXISTS items (
|
||||||
|
55 id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
56 name TEXT NOT NULL,
|
||||||
|
57 price REAL NOT NULL
|
||||||
|
58 );
|
||||||
|
59 ");
|
||||||
|
60
|
||||||
|
61 if ($this->pdo->query("SELECT COUNT(id) FROM items;")-
|
||||||
|
>fetchColumn() == 0) {
|
||||||
|
62 $this->pdo->exec("
|
||||||
|
63 INSERT INTO items (name, price) VALUES ('Молоко
|
||||||
|
пастеризоване', 12);
|
||||||
|
64 INSERT INTO items (name, price) VALUES ('Хліб
|
||||||
|
чорний', 9);
|
||||||
|
65 INSERT INTO items (name, price) VALUES ('Сир
|
||||||
|
білий', 21);
|
||||||
|
66 INSERT INTO items (name, price) VALUES ('Сметана
|
||||||
|
20%', 25);
|
||||||
|
67 INSERT INTO items (name, price) VALUES ('Кефір
|
||||||
|
1%', 19);
|
||||||
|
68 INSERT INTO items (name, price) VALUES ('Вода
|
||||||
|
газована', 18);
|
||||||
|
69 INSERT INTO items (name, price) VALUES ('Печиво
|
||||||
|
\"Весна\"', 14);
|
||||||
|
70 ");
|
||||||
|
71 }
|
||||||
|
72 } catch (PDOException $e) {
|
||||||
|
73 throw new DbException("Error initialising items table.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
74 }
|
||||||
|
75
|
||||||
|
76 try {
|
||||||
|
77 $this->pdo->exec("
|
||||||
|
78 CREATE TABLE IF NOT EXISTS cart (
|
||||||
|
79 id INTEGER NOT NULL UNIQUE,
|
||||||
|
80 count INTEGER NOT NULL,
|
||||||
|
81 FOREIGN KEY(id) REFERENCES item(id)
|
||||||
|
82 );
|
||||||
|
83 ");
|
||||||
|
84 } catch (PDOException $e) {
|
||||||
|
8
|
||||||
|
|
||||||
|
85 throw new DbException("Error initialising cart table.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
86 }
|
||||||
|
87 }
|
||||||
|
88
|
||||||
|
89 /**
|
||||||
|
90 * Updates user information in the database
|
||||||
|
91 *
|
||||||
|
92 * @param string $name
|
||||||
|
93 * @param int $age
|
||||||
|
94 * @return void
|
||||||
|
95 * @throws DbException If there's a database error.
|
||||||
|
96 */
|
||||||
|
97 public function update_user($name, $age): void
|
||||||
|
98 {
|
||||||
|
99 try {
|
||||||
|
100 $stmt = $this->pdo->prepare("UPDATE settings SET name
|
||||||
|
= :name, age = :age;");
|
||||||
|
101
|
||||||
|
102 $stmt->bindParam(':name', $name, PDO::PARAM_STR);
|
||||||
|
103 $stmt->bindParam(':age', $age, PDO::PARAM_INT);
|
||||||
|
104
|
||||||
|
105 $stmt->execute();
|
||||||
|
106 } catch (PDOException $e) {
|
||||||
|
107 throw new DbException("Error updating user info.\nCaused
|
||||||
|
by: " . $e->getMessage());
|
||||||
|
108 }
|
||||||
|
109 }
|
||||||
|
110
|
||||||
|
111 /**
|
||||||
|
112 * Fetches all items from the database.
|
||||||
|
113 *
|
||||||
|
114 * @return array[]
|
||||||
|
115 * @throws DbException If there's a database error.
|
||||||
|
116 */
|
||||||
|
117 public function get_items(): array
|
||||||
|
118 {
|
||||||
|
119 try {
|
||||||
|
120 $stmt = $this->pdo->query("SELECT id, name, price FROM
|
||||||
|
items ORDER BY id;");
|
||||||
|
121 return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
122 } catch (PDOException $e) {
|
||||||
|
123 throw new DbException("Error retrieving data from the
|
||||||
|
items table.\nCaused by: " . $e->getMessage());
|
||||||
|
124 }
|
||||||
|
125 }
|
||||||
|
126
|
||||||
|
127 /**
|
||||||
|
128 * Fetches all items in the cart from the database without price
|
||||||
|
info.
|
||||||
|
129 *
|
||||||
|
130 * @return array[]
|
||||||
|
131 * @throws DbException If there's a database error.
|
||||||
|
132 */
|
||||||
|
133 public function get_cart_no_price(): array
|
||||||
|
134 {
|
||||||
|
9
|
||||||
|
|
||||||
|
135 try {
|
||||||
|
136 $stmt = $this->pdo->query(
|
||||||
|
137 "SELECT name, count FROM cart
|
||||||
|
138 INNER JOIN items ON cart.id = items.id
|
||||||
|
139 ORDER BY cart.id;"
|
||||||
|
140 );
|
||||||
|
141 return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
142 } catch (PDOException $e) {
|
||||||
|
143 throw new DbException("Error inserting init data in the
|
||||||
|
items table.\nCaused by: " . $e->getMessage());
|
||||||
|
144 }
|
||||||
|
145 }
|
||||||
|
146
|
||||||
|
147 /**
|
||||||
|
148 * Fetches all items in the cart from the database.
|
||||||
|
149 *
|
||||||
|
150 * @return array[]
|
||||||
|
151 * @throws DbException If there's a database error.
|
||||||
|
152 */
|
||||||
|
153 public function get_cart(): array
|
||||||
|
154 {
|
||||||
|
155 try {
|
||||||
|
156 $stmt = $this->pdo->query(
|
||||||
|
157 "SELECT
|
||||||
|
158 cart.id, name, price, count, price*count as
|
||||||
|
total_price
|
||||||
|
159 FROM cart
|
||||||
|
160 INNER JOIN items ON cart.id = items.id
|
||||||
|
161 ORDER BY cart.id;"
|
||||||
|
162 );
|
||||||
|
163 return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
164 } catch (PDOException $e) {
|
||||||
|
165 throw new DbException("Error inserting init data in the
|
||||||
|
items table.\nCaused by: " . $e->getMessage());
|
||||||
|
166 }
|
||||||
|
167 }
|
||||||
|
168
|
||||||
|
169 /**
|
||||||
|
170 * Add item to the cart
|
||||||
|
171 *
|
||||||
|
172 * @param int $id
|
||||||
|
173 * @param int $count
|
||||||
|
174 *
|
||||||
|
175 * @return bool
|
||||||
|
176 * @throws DbException If there's a database error.
|
||||||
|
177 */
|
||||||
|
178 public function add_to_cart($id, $count): bool
|
||||||
|
179 {
|
||||||
|
180 try {
|
||||||
|
181 $stmt = $this->pdo->prepare(
|
||||||
|
182 "INSERT INTO cart
|
||||||
|
183 (id, count)
|
||||||
|
184 VALUES
|
||||||
|
185 (:id, :count)
|
||||||
|
186 ON CONFLICT(id)
|
||||||
|
187 DO UPDATE SET
|
||||||
|
10
|
||||||
|
|
||||||
|
188 count = :count
|
||||||
|
189 WHERE id = :id;"
|
||||||
|
190 );
|
||||||
|
191 return $stmt->execute(['id' => $id, 'count' => $count]);
|
||||||
|
192 } catch (PDOException $e) {
|
||||||
|
193 throw new DbException("Error adding item to the cart.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
194 }
|
||||||
|
195 }
|
||||||
|
196
|
||||||
|
197 /**
|
||||||
|
198 * Remove an item from the cart
|
||||||
|
199 *
|
||||||
|
200 * @param int $id
|
||||||
|
201 *
|
||||||
|
202 * @return bool
|
||||||
|
203 * @throws DbException If there's a database error.
|
||||||
|
204 */
|
||||||
|
205 public function remove_from_cart($id): bool
|
||||||
|
206 {
|
||||||
|
207 try {
|
||||||
|
208 $stmt = $this->pdo->prepare("DELETE FROM cart WHERE id
|
||||||
|
= :id");
|
||||||
|
209 return $stmt->execute(['id' => $id]);
|
||||||
|
210 } catch (PDOException $e) {
|
||||||
|
211 throw new DbException("Error removimg item from the cart.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
212 }
|
||||||
|
213 }
|
||||||
|
214 }
|
||||||
|
215
|
||||||
|
216 class App
|
||||||
|
217 {
|
||||||
|
218 private $db;
|
||||||
|
219 private $state;
|
||||||
|
220
|
||||||
|
221 private $menu_ops = <<<'END'
|
||||||
|
222 1 Вибрати товари
|
||||||
|
223 2 Отримати підсумковий рахунок
|
||||||
|
224 3 Налаштувати свій профіль
|
||||||
|
225 0 Вийти з програми
|
||||||
|
226 END;
|
||||||
|
227 private $hello = <<<'END'
|
||||||
|
228 ################################
|
||||||
|
229 # ПРОДОВОЛЬЧИЙ МАГАЗИН "ВЕСНА" #
|
||||||
|
230 ################################
|
||||||
|
231 END;
|
||||||
|
232
|
||||||
|
233
|
||||||
|
234 /**
|
||||||
|
235 * @param string $db_path
|
||||||
|
236 */
|
||||||
|
237 public function __construct($db_path)
|
||||||
|
238 {
|
||||||
|
239 try {
|
||||||
|
240 $this->db = new DB($db_path);
|
||||||
|
11
|
||||||
|
|
||||||
|
241 $this->state = State::Hello;
|
||||||
|
242 } catch (DbException $e) {
|
||||||
|
243 throw new AppException("Error initializing app.\nCaused
|
||||||
|
by: " . $e);
|
||||||
|
244 }
|
||||||
|
245 }
|
||||||
|
246
|
||||||
|
247 public function poll(): void
|
||||||
|
248 {
|
||||||
|
249 while ($this->state != State::Exit) {
|
||||||
|
250 switch ($this->state) {
|
||||||
|
251 case State::Hello:
|
||||||
|
252 $this->hello();
|
||||||
|
253 break;
|
||||||
|
254 case State::Menu:
|
||||||
|
255 $this->menu();
|
||||||
|
256 break;
|
||||||
|
257 case State::Items:
|
||||||
|
258 $this->items();
|
||||||
|
259 break;
|
||||||
|
260 case State::Checkout:
|
||||||
|
261 $this->checkout();
|
||||||
|
262 break;
|
||||||
|
263 case State::Settins:
|
||||||
|
264 $this->settings();
|
||||||
|
265 break;
|
||||||
|
266
|
||||||
|
267 default:
|
||||||
|
268 break;
|
||||||
|
269 }
|
||||||
|
270 }
|
||||||
|
271 }
|
||||||
|
272
|
||||||
|
273 private function menu(): void
|
||||||
|
274 {
|
||||||
|
275 echo "\n";
|
||||||
|
276 echo "$this->menu_ops\n";
|
||||||
|
277
|
||||||
|
278 $op = readline('Введіть команду: ');
|
||||||
|
279 switch ($op) {
|
||||||
|
280 case '1':
|
||||||
|
281 $this->state = State::Items;
|
||||||
|
282 break;
|
||||||
|
283 case '2':
|
||||||
|
284 $this->state = State::Checkout;
|
||||||
|
285 break;
|
||||||
|
286 case '3':
|
||||||
|
287 $this->state = State::Settins;
|
||||||
|
288 break;
|
||||||
|
289 case '0':
|
||||||
|
290 $this->state = State::Exit;
|
||||||
|
291 break;
|
||||||
|
292
|
||||||
|
293 default:
|
||||||
|
294 echo "ПОМИЛКА! Введіть правильну команду\n";
|
||||||
|
295 break;
|
||||||
|
12
|
||||||
|
|
||||||
|
296 }
|
||||||
|
297
|
||||||
|
298 echo "\n";
|
||||||
|
299 }
|
||||||
|
300 private function hello(): void
|
||||||
|
301 {
|
||||||
|
302 echo "$this->hello\n";
|
||||||
|
303 $this->state = State::Menu;
|
||||||
|
304 }
|
||||||
|
305 private function items(): void
|
||||||
|
306 {
|
||||||
|
307 $items = $this->db->get_items();
|
||||||
|
308 array_unshift($items, ['id' => "№", 'name' => "НАЗВА", 'price'
|
||||||
|
=> "ЦІНА"]);
|
||||||
|
309 array_push($items, ['id' => " ", 'name' => "-----------",
|
||||||
|
'price' => ""]);
|
||||||
|
310 array_push($items, ['id' => "0", 'name' => "ПОВЕРНУТИСЯ",
|
||||||
|
'price' => ""]);
|
||||||
|
311 $columns = $this->count_columns($items);
|
||||||
|
312
|
||||||
|
313 while (true) {
|
||||||
|
314 $this->print_lits($items, $columns);
|
||||||
|
315
|
||||||
|
316 $id = readline("Виберіть товар: ");
|
||||||
|
317
|
||||||
|
318 if ($id == '0') {
|
||||||
|
319 break;
|
||||||
|
320 }
|
||||||
|
321
|
||||||
|
322 echo "\n";
|
||||||
|
323
|
||||||
|
324 $selected = null;
|
||||||
|
325 foreach ($items as $item) {
|
||||||
|
326 if ($item['id'] === (int)$id)
|
||||||
|
327 $selected = $item;
|
||||||
|
328 }
|
||||||
|
329
|
||||||
|
330 if ($selected == null) {
|
||||||
|
331 echo "ПОМИЛКА! ВКАЗАНО НЕПРАВИЛЬНИЙ НОМЕР ТОВАРУ\n";
|
||||||
|
332 continue;
|
||||||
|
333 }
|
||||||
|
334
|
||||||
|
335 echo "Вибрано: {$selected['name']}\n";
|
||||||
|
336
|
||||||
|
337 $count = (int)readline("Введіть кількість, штук: ");
|
||||||
|
338
|
||||||
|
339 if ($count > 100) {
|
||||||
|
340 echo "ПОМИЛКА! Не можна додати більше 100 одиниць
|
||||||
|
товару в кошик\n";
|
||||||
|
341 continue;
|
||||||
|
342 }
|
||||||
|
343
|
||||||
|
344 if ($count < 0) {
|
||||||
|
345 echo "ПОМИЛКА! Кількість не може бути від'ємною\n";
|
||||||
|
346 continue;
|
||||||
|
347 }
|
||||||
|
13
|
||||||
|
|
||||||
|
348
|
||||||
|
349 if ($count == 0) {
|
||||||
|
350 echo "ВИДАЛЯЮ З КОШИКА\n";
|
||||||
|
351 $this->db->remove_from_cart($id);
|
||||||
|
352 } else {
|
||||||
|
353 $this->db->add_to_cart($id, $count);
|
||||||
|
354 }
|
||||||
|
355
|
||||||
|
356 $cart = $this->db->get_cart_no_price();
|
||||||
|
357 if (count($cart) == 0) {
|
||||||
|
358 echo "КОШИК ПОРОЖНІЙ\n";
|
||||||
|
359 } else {
|
||||||
|
360 echo "\nУ КОШИКУ:\n";
|
||||||
|
361 array_unshift($cart, ['name' => "НАЗВА", 'count' =>
|
||||||
|
"КІЛЬКІСТЬ"]);
|
||||||
|
362 $cart_columns = $this->count_columns($cart);
|
||||||
|
363 $this->print_lits($cart, $cart_columns);
|
||||||
|
364 echo "\n";
|
||||||
|
365 }
|
||||||
|
366 }
|
||||||
|
367
|
||||||
|
368 $this->state = State::Menu;
|
||||||
|
369 }
|
||||||
|
370 private function checkout(): void
|
||||||
|
371 {
|
||||||
|
372 $cart = $this->db->get_cart();
|
||||||
|
373 if (count($cart) == 0) {
|
||||||
|
374 echo "КОШИК ПОРОЖНІЙ\n";
|
||||||
|
375 $this->state = State::Menu;
|
||||||
|
376 return;
|
||||||
|
377 } else {
|
||||||
|
378 echo "У КОШИКУ:\n";
|
||||||
|
379 array_unshift($cart, ['id' => "№", 'name' => "НАЗВА",
|
||||||
|
'price' => "ЦІНА", 'count' => "КІЛЬКІСТЬ", 'total_price' =>
|
||||||
|
"ВАРТІСТЬ"]);
|
||||||
|
380 $cart_columns = $this->count_columns($cart);
|
||||||
|
381 $this->print_lits($cart, $cart_columns);
|
||||||
|
382 }
|
||||||
|
383
|
||||||
|
384 $total_price = array_reduce($cart, function ($carry, $item) {
|
||||||
|
385 return $carry + (int)$item['total_price'];
|
||||||
|
386 }, 0);
|
||||||
|
387 echo "РАЗОМ ДО СПЛАТИ: {$total_price}\n";
|
||||||
|
388
|
||||||
|
389 $this->state = State::Menu;
|
||||||
|
390 }
|
||||||
|
391 private function settings(): void
|
||||||
|
392 {
|
||||||
|
393 while (true) {
|
||||||
|
394 $name = readline("Ваше ім'я: ");
|
||||||
|
395 if ($name !== "" && preg_match("/[a-zA-Z]+/", $name))
|
||||||
|
396 break;
|
||||||
|
397 }
|
||||||
|
398
|
||||||
|
399 while (true) {
|
||||||
|
400 $age = readline("Ваш вік: ");
|
||||||
|
14
|
||||||
|
|
||||||
|
401
|
||||||
|
402 if (!filter_var($age, FILTER_VALIDATE_INT)) {
|
||||||
|
403 echo "ПОМИЛКА! Вік користувача потрібно вказати
|
||||||
|
числом\n\n";
|
||||||
|
404 continue;
|
||||||
|
405 }
|
||||||
|
406
|
||||||
|
407 if ($age < 7 || $age > 150) {
|
||||||
|
408 echo "ПОМИЛКА! Користувач повинен мати вік від 7 та до
|
||||||
|
150 років\n\n";
|
||||||
|
409 continue;
|
||||||
|
410 }
|
||||||
|
411
|
||||||
|
412 break;
|
||||||
|
413 }
|
||||||
|
414
|
||||||
|
415 echo "\n";
|
||||||
|
416
|
||||||
|
417 $this->db->update_user($name, $age);
|
||||||
|
418
|
||||||
|
419 $this->state = State::Menu;
|
||||||
|
420 }
|
||||||
|
421
|
||||||
|
422 /**
|
||||||
|
423 * @param array<array> $items
|
||||||
|
424 * @return array<int>
|
||||||
|
425 */
|
||||||
|
426 private function count_columns($items): array
|
||||||
|
427 {
|
||||||
|
428 $columns = [];
|
||||||
|
429 foreach ($items as $item) {
|
||||||
|
430 foreach ($item as $field => $value) {
|
||||||
|
431 if (!key_exists($field, $columns))
|
||||||
|
432 $columns[$field] = mb_strlen($value);
|
||||||
|
433 else
|
||||||
|
434 $columns[$field] = max(mb_strlen($value),
|
||||||
|
$columns[$field]);
|
||||||
|
435 }
|
||||||
|
436 }
|
||||||
|
437
|
||||||
|
438 return $columns;
|
||||||
|
439 }
|
||||||
|
440 /**
|
||||||
|
441 * @param array $element
|
||||||
|
442 * @param array<int> $columns
|
||||||
|
443 */
|
||||||
|
444 private function pad_row($element, $columns): string
|
||||||
|
445 {
|
||||||
|
446 $result = [];
|
||||||
|
447 foreach ($element as $field => $value)
|
||||||
|
448 $result[] = mb_str_pad($value, $columns[$field], ' ',
|
||||||
|
STR_PAD_RIGHT);
|
||||||
|
449
|
||||||
|
450 return implode(" ", $result);
|
||||||
|
451 }
|
||||||
|
452 /**
|
||||||
|
15
|
||||||
|
|
||||||
|
453 * @param array<array> $items
|
||||||
|
454 * @param array<int> $columns
|
||||||
|
455 */
|
||||||
|
456 private function print_lits($items, $columns): void
|
||||||
|
457 {
|
||||||
|
458 foreach ($items as $item)
|
||||||
|
459 echo $this->pad_row($item, $columns) . "\n";
|
||||||
|
460 }
|
||||||
|
461 }
|
||||||
|
462
|
||||||
|
463 try {
|
||||||
|
464 $app = new App("db.sqlite");
|
||||||
|
465 } catch (AppException $e) {
|
||||||
|
466 echo $e;
|
||||||
|
467 exit(1);
|
||||||
|
468 }
|
||||||
|
469
|
||||||
|
470 $app->poll();
|
||||||
|
|
||||||
@@ -0,0 +1,644 @@
|
|||||||
|
#import "@local/nure:0.1.0": *
|
||||||
|
|
||||||
|
#show: pz-lb.with(
|
||||||
|
title: [Продовольчий магазин "Весна"],
|
||||||
|
subject: "СМП",
|
||||||
|
doctype: "ЛБ",
|
||||||
|
worknumber: 2,
|
||||||
|
edu_program: "ПЗПІ",
|
||||||
|
university: "ХНУРЕ",
|
||||||
|
mentors: (
|
||||||
|
(
|
||||||
|
name: "Сокорчук І. П.",
|
||||||
|
degree: "Старший викладач кафедри ПІ",
|
||||||
|
gender: "m",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
authors: (
|
||||||
|
(
|
||||||
|
name: "Ситник Є. С.",
|
||||||
|
course: 2,
|
||||||
|
edu: "ПЗПІ",
|
||||||
|
gender: "m",
|
||||||
|
group: "23-2",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
#v(-spacing)
|
||||||
|
== Історія змін
|
||||||
|
#figure(
|
||||||
|
table(
|
||||||
|
align: left,
|
||||||
|
columns: (auto, 1fr, 1fr, 3fr),
|
||||||
|
table.header([№], [Дата], [Версія звіту], [Опис змін та виправлень]),
|
||||||
|
[1], [03.03.2025], [0.1], [Створено звіт],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
== Мета роботи
|
||||||
|
Лабораторна робота полягає у розробці консольного застосунку "Продовольчий магазин "Весна"" засобами мови програмування "PHP".
|
||||||
|
|
||||||
|
== Хід роботи
|
||||||
|
#v(-spacing)
|
||||||
|
=== Архітектура програми
|
||||||
|
|
||||||
|
Для реалізації консольного застосунку було обрано об'єктно-орієнтований підхід з використанням наступних компонентів:
|
||||||
|
|
||||||
|
==== Класи та їх призначення:
|
||||||
|
|
||||||
|
- *DB* -- клас для роботи з базою даних "SQLite", що забезпечує збереження даних про товари, кошик користувача та налаштування профілю;
|
||||||
|
- *App* -- основний клас застосунку, що реалізує логіку роботи програми та взаємодію з користувачем;
|
||||||
|
- *DbException* та *AppException* -- класи винятків для обробки помилок.
|
||||||
|
|
||||||
|
==== Enum State:
|
||||||
|
Для управління станами програми використовується перелічення "State" з наступними значеннями:
|
||||||
|
- *Hello* -- привітальний екран;
|
||||||
|
- *Menu* -- головне меню;
|
||||||
|
- *Items* -- вибір товарів;
|
||||||
|
- *Checkout* -- формування рахунку;
|
||||||
|
- *Settings* -- налаштування профілю;
|
||||||
|
- *Exit* -- вихід з програми.
|
||||||
|
|
||||||
|
=== Структура бази даних
|
||||||
|
|
||||||
|
Програма використовує базу даних SQLite з трьома таблицями:
|
||||||
|
|
||||||
|
- *settings* -- зберігає інформацію про користувача (ім'я та вік);
|
||||||
|
- *items* -- містить каталог товарів з їх назвами та цінами;
|
||||||
|
- *cart* -- зберігає товари, додані до кошика, з їх кількістю.
|
||||||
|
|
||||||
|
=== Основний функціонал
|
||||||
|
|
||||||
|
==== Головне меню:
|
||||||
|
Програма відображає заголовок магазину та пропонує користувачу чотири основні опції:
|
||||||
|
- вибір товарів для покупки;
|
||||||
|
- перегляд підсумкового рахунку;
|
||||||
|
- налаштування профілю користувача;
|
||||||
|
- вихід з програми.
|
||||||
|
|
||||||
|
==== Вибір товарів:
|
||||||
|
При виборі першого пункту меню користувач потрапляє в режим покупки товарів, де:
|
||||||
|
- відображається список доступних товарів з цінами;
|
||||||
|
- можна вибрати товар за номером та вказати кількість;
|
||||||
|
- товари додаються до кошика;
|
||||||
|
- при введенні кількості "0" товар видаляється з кошика;
|
||||||
|
- реалізована валідація введених даних.
|
||||||
|
|
||||||
|
==== Підсумковий рахунок:
|
||||||
|
Другий пункт меню формує детальний рахунок з інформацією про:
|
||||||
|
- номер, назву та ціну кожного товару;
|
||||||
|
- кількість товару в кошику;
|
||||||
|
- загальну вартість кожного товару;
|
||||||
|
- підсумкову суму до сплати.
|
||||||
|
|
||||||
|
==== Налаштування профілю:
|
||||||
|
Третій пункт дозволяє користувачу вказати своє ім'я та вік з валідацією:
|
||||||
|
- ім'я повинно містити хоча б одну літеру;
|
||||||
|
- вік повинен бути в діапазоні від 7 до 150 років.
|
||||||
|
|
||||||
|
=== Технічні деталі
|
||||||
|
|
||||||
|
Програма написана з дотриманням сучасних стандартів "PHP":
|
||||||
|
- використання строгої типізації;
|
||||||
|
- документування методів за допомогою "PHPDoc";
|
||||||
|
- дотримання принципів "SOLID";
|
||||||
|
- розділення логіки на окремі методи для кращої читабельності.
|
||||||
|
|
||||||
|
== Висновки
|
||||||
|
|
||||||
|
Під час даної лабораторної роботи я вивчив основні принципи роботи з "PHP" для написання консольних програм. Зокрема, було освоєно:
|
||||||
|
|
||||||
|
- створення об'єктно-орієнтованих консольних застосунків на "PHP";
|
||||||
|
- роботу з базою даних "SQLite" через "PDO";
|
||||||
|
- обробку користувацького вводу та валідацію даних;
|
||||||
|
- використання перелічень ("enum") для управління станами програми;
|
||||||
|
- реалізацію системи обробки винятків;
|
||||||
|
- форматування виводу в консолі для створення зручного інтерфейсу.
|
||||||
|
|
||||||
|
Програмне забезпечення, реалізоване протягом даної лабораторної роботи, повністю відповідає поставленим вимогам та забезпечує всі необхідні функції для роботи з продовольчим магазином. Застосунок має зручний інтерфейс, надійну систему валідації даних та ефективно працює з базою даних для збереження інформації про товари та користувача.
|
||||||
|
|
||||||
|
#set raw(tab-size: 8)
|
||||||
|
#show raw.where(block: true): code => {
|
||||||
|
// set text(11pt, font: "Caladea", top-edge: 1em, bottom-edge: 0em)
|
||||||
|
set text(11pt, top-edge: 1em, bottom-edge: 0em)
|
||||||
|
set par(leading: 0.17em)
|
||||||
|
|
||||||
|
grid(
|
||||||
|
columns: (auto, auto),
|
||||||
|
column-gutter: 1em,
|
||||||
|
row-gutter: 0.17em,
|
||||||
|
align: (right, raw.align),
|
||||||
|
..for line in code.lines {
|
||||||
|
(
|
||||||
|
text(fill: gray)[#line.number],
|
||||||
|
{
|
||||||
|
set text(weight: "semibold")
|
||||||
|
line.body
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#show: appendices_style
|
||||||
|
= Відеозапис
|
||||||
|
Відеозапис презентації результатів лабораторної роботи: https://youtu.be/CMGeL1OdyvI
|
||||||
|
|
||||||
|
*Хронологічний опис відеозапису:*
|
||||||
|
|
||||||
|
00:00 -- Вступ та опис завдання
|
||||||
|
|
||||||
|
00:19 -- Архітектура програми
|
||||||
|
|
||||||
|
00:39 -- Робота з базою даних
|
||||||
|
|
||||||
|
01:43 -- Реалізація станів програми
|
||||||
|
|
||||||
|
02:17 -- Формування списку товарів
|
||||||
|
|
||||||
|
03:13 -- Додавання товарів до кошика
|
||||||
|
|
||||||
|
04:15 -- Робота з кошиком
|
||||||
|
|
||||||
|
05:10 -- Налаштування профілю користувача
|
||||||
|
|
||||||
|
05:38 -- Демонстрація роботи програми
|
||||||
|
|
||||||
|
= Програмний код
|
||||||
|
== Скрипт з реалізацією програми
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab2/smp-pzpi-23-2-sytnyk-yehor-lab2/smp-pzpi-23-2-sytnyk-yehor-lab2-code
|
||||||
|
|
||||||
|
#v(spacing)
|
||||||
|
|
||||||
|
```
|
||||||
|
#!/usr/bin/php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class DbException extends Exception {}
|
||||||
|
class AppException extends Exception {}
|
||||||
|
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
case Hello;
|
||||||
|
case Menu;
|
||||||
|
case Items;
|
||||||
|
case Checkout;
|
||||||
|
case Settins;
|
||||||
|
case Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DB
|
||||||
|
{
|
||||||
|
private $pdo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes database
|
||||||
|
*
|
||||||
|
* @param string $db_path
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function __construct($db_path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->pdo = new PDO("sqlite:" . $db_path);
|
||||||
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Connection to DB failed.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
name TEXT,
|
||||||
|
age TEXT
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
if ($this->pdo->query("SELECT COUNT(*) FROM settings;")->fetchColumn() == 0) {
|
||||||
|
$this->pdo->exec("INSERT INTO settings (name, age) VALUES ('user', 0);");
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising settings table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS items (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
price REAL NOT NULL
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
if ($this->pdo->query("SELECT COUNT(id) FROM items;")->fetchColumn() == 0) {
|
||||||
|
$this->pdo->exec("
|
||||||
|
INSERT INTO items (name, price) VALUES ('Молоко пастеризоване', 12);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Хліб чорний', 9);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сир білий', 21);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сметана 20%', 25);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Кефір 1%', 19);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Вода газована', 18);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Печиво \"Весна\"', 14);
|
||||||
|
");
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS cart (
|
||||||
|
id INTEGER NOT NULL UNIQUE,
|
||||||
|
count INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(id) REFERENCES item(id)
|
||||||
|
);
|
||||||
|
");
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising cart table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates user information in the database
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param int $age
|
||||||
|
* @return void
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function update_user($name, $age): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("UPDATE settings SET name = :name, age = :age;");
|
||||||
|
|
||||||
|
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':age', $age, PDO::PARAM_INT);
|
||||||
|
|
||||||
|
$stmt->execute();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error updating user info.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items from the database.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_items(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query("SELECT id, name, price FROM items ORDER BY id;");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error retrieving data from the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items in the cart from the database without price info.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart_no_price(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT name, count FROM cart
|
||||||
|
INNER JOIN items ON cart.id = items.id
|
||||||
|
ORDER BY cart.id;"
|
||||||
|
);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error inserting init data in the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items in the cart from the database.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT
|
||||||
|
cart.id, name, price, count, price*count as total_price
|
||||||
|
FROM cart
|
||||||
|
INNER JOIN items ON cart.id = items.id
|
||||||
|
ORDER BY cart.id;"
|
||||||
|
);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error inserting init data in the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add item to the cart
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @param int $count
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function add_to_cart($id, $count): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
"INSERT INTO cart
|
||||||
|
(id, count)
|
||||||
|
VALUES
|
||||||
|
(:id, :count)
|
||||||
|
ON CONFLICT(id)
|
||||||
|
DO UPDATE SET
|
||||||
|
count = :count
|
||||||
|
WHERE id = :id;"
|
||||||
|
);
|
||||||
|
return $stmt->execute(['id' => $id, 'count' => $count]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error adding item to the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cart
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function remove_from_cart($id): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("DELETE FROM cart WHERE id = :id");
|
||||||
|
return $stmt->execute(['id' => $id]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error removimg item from the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class App
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $state;
|
||||||
|
|
||||||
|
private $menu_ops = <<<'END'
|
||||||
|
1 Вибрати товари
|
||||||
|
2 Отримати підсумковий рахунок
|
||||||
|
3 Налаштувати свій профіль
|
||||||
|
0 Вийти з програми
|
||||||
|
END;
|
||||||
|
private $hello = <<<'END'
|
||||||
|
################################
|
||||||
|
# ПРОДОВОЛЬЧИЙ МАГАЗИН "ВЕСНА" #
|
||||||
|
################################
|
||||||
|
END;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $db_path
|
||||||
|
*/
|
||||||
|
public function __construct($db_path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->db = new DB($db_path);
|
||||||
|
$this->state = State::Hello;
|
||||||
|
} catch (DbException $e) {
|
||||||
|
throw new AppException("Error initializing app.\nCaused by: " . $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function poll(): void
|
||||||
|
{
|
||||||
|
while ($this->state != State::Exit) {
|
||||||
|
switch ($this->state) {
|
||||||
|
case State::Hello:
|
||||||
|
$this->hello();
|
||||||
|
break;
|
||||||
|
case State::Menu:
|
||||||
|
$this->menu();
|
||||||
|
break;
|
||||||
|
case State::Items:
|
||||||
|
$this->items();
|
||||||
|
break;
|
||||||
|
case State::Checkout:
|
||||||
|
$this->checkout();
|
||||||
|
break;
|
||||||
|
case State::Settins:
|
||||||
|
$this->settings();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function menu(): void
|
||||||
|
{
|
||||||
|
echo "\n";
|
||||||
|
echo "$this->menu_ops\n";
|
||||||
|
|
||||||
|
$op = readline('Введіть команду: ');
|
||||||
|
switch ($op) {
|
||||||
|
case '1':
|
||||||
|
$this->state = State::Items;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
$this->state = State::Checkout;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
$this->state = State::Settins;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
$this->state = State::Exit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo "ПОМИЛКА! Введіть правильну команду\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
private function hello(): void
|
||||||
|
{
|
||||||
|
echo "$this->hello\n";
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function items(): void
|
||||||
|
{
|
||||||
|
$items = $this->db->get_items();
|
||||||
|
array_unshift($items, ['id' => "№", 'name' => "НАЗВА", 'price' => "ЦІНА"]);
|
||||||
|
array_push($items, ['id' => " ", 'name' => "-----------", 'price' => ""]);
|
||||||
|
array_push($items, ['id' => "0", 'name' => "ПОВЕРНУТИСЯ", 'price' => ""]);
|
||||||
|
$columns = $this->count_columns($items);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$this->print_lits($items, $columns);
|
||||||
|
|
||||||
|
$id = readline("Виберіть товар: ");
|
||||||
|
|
||||||
|
if ($id == '0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
$selected = null;
|
||||||
|
foreach ($items as $item) {
|
||||||
|
if ($item['id'] === (int)$id)
|
||||||
|
$selected = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($selected == null) {
|
||||||
|
echo "ПОМИЛКА! ВКАЗАНО НЕПРАВИЛЬНИЙ НОМЕР ТОВАРУ\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Вибрано: {$selected['name']}\n";
|
||||||
|
|
||||||
|
$count = (int)readline("Введіть кількість, штук: ");
|
||||||
|
|
||||||
|
if ($count > 100) {
|
||||||
|
echo "ПОМИЛКА! Не можна додати більше 100 одиниць товару в кошик\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($count < 0) {
|
||||||
|
echo "ПОМИЛКА! Кількість не може бути від'ємною\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($count == 0) {
|
||||||
|
echo "ВИДАЛЯЮ З КОШИКА\n";
|
||||||
|
$this->db->remove_from_cart($id);
|
||||||
|
} else {
|
||||||
|
$this->db->add_to_cart($id, $count);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart = $this->db->get_cart_no_price();
|
||||||
|
if (count($cart) == 0) {
|
||||||
|
echo "КОШИК ПОРОЖНІЙ\n";
|
||||||
|
} else {
|
||||||
|
echo "\nУ КОШИКУ:\n";
|
||||||
|
array_unshift($cart, ['name' => "НАЗВА", 'count' => "КІЛЬКІСТЬ"]);
|
||||||
|
$cart_columns = $this->count_columns($cart);
|
||||||
|
$this->print_lits($cart, $cart_columns);
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function checkout(): void
|
||||||
|
{
|
||||||
|
$cart = $this->db->get_cart();
|
||||||
|
if (count($cart) == 0) {
|
||||||
|
echo "КОШИК ПОРОЖНІЙ\n";
|
||||||
|
$this->state = State::Menu;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
echo "У КОШИКУ:\n";
|
||||||
|
array_unshift($cart, ['id' => "№", 'name' => "НАЗВА", 'price' => "ЦІНА", 'count' => "КІЛЬКІСТЬ", 'total_price' => "ВАРТІСТЬ"]);
|
||||||
|
$cart_columns = $this->count_columns($cart);
|
||||||
|
$this->print_lits($cart, $cart_columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
$total_price = array_reduce($cart, function ($carry, $item) {
|
||||||
|
return $carry + (int)$item['total_price'];
|
||||||
|
}, 0);
|
||||||
|
echo "РАЗОМ ДО СПЛАТИ: {$total_price}\n";
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function settings(): void
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
$name = readline("Ваше ім'я: ");
|
||||||
|
if ($name !== "" && preg_match("/[a-zA-Z]+/", $name))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$age = readline("Ваш вік: ");
|
||||||
|
|
||||||
|
if (!filter_var($age, FILTER_VALIDATE_INT)) {
|
||||||
|
echo "ПОМИЛКА! Вік користувача потрібно вказати числом\n\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($age < 7 || $age > 150) {
|
||||||
|
echo "ПОМИЛКА! Користувач повинен мати вік від 7 та до 150 років\n\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
$this->db->update_user($name, $age);
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<array> $items
|
||||||
|
* @return array<int>
|
||||||
|
*/
|
||||||
|
private function count_columns($items): array
|
||||||
|
{
|
||||||
|
$columns = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
foreach ($item as $field => $value) {
|
||||||
|
if (!key_exists($field, $columns))
|
||||||
|
$columns[$field] = mb_strlen($value);
|
||||||
|
else
|
||||||
|
$columns[$field] = max(mb_strlen($value), $columns[$field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param array $element
|
||||||
|
* @param array<int> $columns
|
||||||
|
*/
|
||||||
|
private function pad_row($element, $columns): string
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
foreach ($element as $field => $value)
|
||||||
|
$result[] = mb_str_pad($value, $columns[$field], ' ', STR_PAD_RIGHT);
|
||||||
|
|
||||||
|
return implode(" ", $result);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param array<array> $items
|
||||||
|
* @param array<int> $columns
|
||||||
|
*/
|
||||||
|
private function print_lits($items, $columns): void
|
||||||
|
{
|
||||||
|
foreach ($items as $item)
|
||||||
|
echo $this->pad_row($item, $columns) . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app = new App("db.sqlite");
|
||||||
|
} catch (AppException $e) {
|
||||||
|
echo $e;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$app->poll();
|
||||||
|
```
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
> [!NOTE]
|
||||||
|
> Викладач: Сокорчук І. П.
|
||||||
|
>
|
||||||
|
> Оцінка: 80
|
||||||
+469
@@ -0,0 +1,469 @@
|
|||||||
|
#!/usr/bin/php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class DbException extends Exception {}
|
||||||
|
class AppException extends Exception {}
|
||||||
|
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
case Hello;
|
||||||
|
case Menu;
|
||||||
|
case Items;
|
||||||
|
case Checkout;
|
||||||
|
case Settins;
|
||||||
|
case Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DB
|
||||||
|
{
|
||||||
|
private $pdo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes database
|
||||||
|
*
|
||||||
|
* @param string $db_path
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function __construct($db_path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->pdo = new PDO("sqlite:" . $db_path);
|
||||||
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Connection to DB failed.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
name TEXT,
|
||||||
|
age TEXT
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
if ($this->pdo->query("SELECT COUNT(*) FROM settings;")->fetchColumn() == 0) {
|
||||||
|
$this->pdo->exec("INSERT INTO settings (name, age) VALUES ('user', 0);");
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising settings table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS items (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
price REAL NOT NULL
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
if ($this->pdo->query("SELECT COUNT(id) FROM items;")->fetchColumn() == 0) {
|
||||||
|
$this->pdo->exec("
|
||||||
|
INSERT INTO items (name, price) VALUES ('Молоко пастеризоване', 12);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Хліб чорний', 9);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сир білий', 21);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сметана 20%', 25);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Кефір 1%', 19);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Вода газована', 18);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Печиво \"Весна\"', 14);
|
||||||
|
");
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS cart (
|
||||||
|
id INTEGER NOT NULL UNIQUE,
|
||||||
|
count INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(id) REFERENCES item(id)
|
||||||
|
);
|
||||||
|
");
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising cart table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates user information in the database
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param int $age
|
||||||
|
* @return void
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function update_user($name, $age): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("UPDATE settings SET name = :name, age = :age;");
|
||||||
|
|
||||||
|
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':age', $age, PDO::PARAM_INT);
|
||||||
|
|
||||||
|
$stmt->execute();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error updating user info.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items from the database.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_items(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query("SELECT id, name, price FROM items ORDER BY id;");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error retrieving data from the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items in the cart from the database without price info.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart_no_price(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT name, count FROM cart
|
||||||
|
INNER JOIN items ON cart.id = items.id
|
||||||
|
ORDER BY cart.id;"
|
||||||
|
);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error inserting init data in the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items in the cart from the database.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT
|
||||||
|
cart.id, name, price, count, price*count as total_price
|
||||||
|
FROM cart
|
||||||
|
INNER JOIN items ON cart.id = items.id
|
||||||
|
ORDER BY cart.id;"
|
||||||
|
);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error inserting init data in the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add item to the cart
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @param int $count
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function add_to_cart($id, $count): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
"INSERT INTO cart
|
||||||
|
(id, count)
|
||||||
|
VALUES
|
||||||
|
(:id, :count)
|
||||||
|
ON CONFLICT(id)
|
||||||
|
DO UPDATE SET
|
||||||
|
count = :count
|
||||||
|
WHERE id = :id;"
|
||||||
|
);
|
||||||
|
return $stmt->execute(['id' => $id, 'count' => $count]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error adding item to the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cart
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function remove_from_cart($id): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("DELETE FROM cart WHERE id = :id");
|
||||||
|
return $stmt->execute(['id' => $id]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error removimg item from the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class App
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $state;
|
||||||
|
|
||||||
|
private $menu_ops = <<<'END'
|
||||||
|
1 Вибрати товари
|
||||||
|
2 Отримати підсумковий рахунок
|
||||||
|
3 Налаштувати свій профіль
|
||||||
|
0 Вийти з програми
|
||||||
|
END;
|
||||||
|
private $hello = <<<'END'
|
||||||
|
################################
|
||||||
|
# ПРОДОВОЛЬЧИЙ МАГАЗИН "ВЕСНА" #
|
||||||
|
################################
|
||||||
|
END;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $db_path
|
||||||
|
*/
|
||||||
|
public function __construct($db_path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->db = new DB($db_path);
|
||||||
|
$this->state = State::Hello;
|
||||||
|
} catch (DbException $e) {
|
||||||
|
throw new AppException("Error initializing app.\nCaused by: " . $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function poll(): void
|
||||||
|
{
|
||||||
|
while ($this->state != State::Exit) {
|
||||||
|
switch ($this->state) {
|
||||||
|
case State::Hello:
|
||||||
|
$this->hello();
|
||||||
|
break;
|
||||||
|
case State::Menu:
|
||||||
|
$this->menu();
|
||||||
|
break;
|
||||||
|
case State::Items:
|
||||||
|
$this->items();
|
||||||
|
break;
|
||||||
|
case State::Checkout:
|
||||||
|
$this->checkout();
|
||||||
|
break;
|
||||||
|
case State::Settins:
|
||||||
|
$this->settings();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function menu(): void
|
||||||
|
{
|
||||||
|
echo "\n";
|
||||||
|
echo "$this->menu_ops\n";
|
||||||
|
|
||||||
|
$op = readline('Введіть команду: ');
|
||||||
|
switch ($op) {
|
||||||
|
case '1':
|
||||||
|
$this->state = State::Items;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
$this->state = State::Checkout;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
$this->state = State::Settins;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
$this->state = State::Exit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo "ПОМИЛКА! Введіть правильну команду\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
private function hello(): void
|
||||||
|
{
|
||||||
|
echo "$this->hello\n";
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function items(): void
|
||||||
|
{
|
||||||
|
$items = $this->db->get_items();
|
||||||
|
array_unshift($items, ['id' => "№", 'name' => "НАЗВА", 'price' => "ЦІНА"]);
|
||||||
|
array_push($items, ['id' => " ", 'name' => "-----------", 'price' => ""]);
|
||||||
|
array_push($items, ['id' => "0", 'name' => "ПОВЕРНУТИСЯ", 'price' => ""]);
|
||||||
|
$columns = $this->count_columns($items);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$this->print_lits($items, $columns);
|
||||||
|
|
||||||
|
$id = readline("Виберіть товар: ");
|
||||||
|
|
||||||
|
if ($id == '0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
$selected = null;
|
||||||
|
foreach ($items as $item) {
|
||||||
|
if ($item['id'] === (int)$id) $selected = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($selected == null) {
|
||||||
|
echo "ПОМИЛКА! ВКАЗАНО НЕПРАВИЛЬНИЙ НОМЕР ТОВАРУ\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Вибрано: {$selected['name']}\n";
|
||||||
|
|
||||||
|
$count = (int)readline("Введіть кількість, штук: ");
|
||||||
|
|
||||||
|
if ($count > 100) {
|
||||||
|
echo "ПОМИЛКА! Не можна додати більше 100 одиниць товару в кошик\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($count < 0) {
|
||||||
|
echo "ПОМИЛКА! Кількість не може бути від'ємною\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($count == 0) {
|
||||||
|
echo "ВИДАЛЯЮ З КОШИКА\n";
|
||||||
|
$this->db->remove_from_cart($id);
|
||||||
|
} else {
|
||||||
|
$this->db->add_to_cart($id, $count);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart = $this->db->get_cart_no_price();
|
||||||
|
if (count($cart) == 0) {
|
||||||
|
echo "КОШИК ПОРОЖНІЙ\n";
|
||||||
|
} else {
|
||||||
|
echo "\nУ КОШИКУ:\n";
|
||||||
|
array_unshift($cart, ['name' => "НАЗВА", 'count' => "КІЛЬКІСТЬ"]);
|
||||||
|
$cart_columns = $this->count_columns($cart);
|
||||||
|
$this->print_lits($cart, $cart_columns);
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function checkout(): void
|
||||||
|
{
|
||||||
|
$cart = $this->db->get_cart();
|
||||||
|
if (count($cart) == 0) {
|
||||||
|
echo "КОШИК ПОРОЖНІЙ\n";
|
||||||
|
$this->state = State::Menu;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
echo "У КОШИКУ:\n";
|
||||||
|
array_unshift($cart, ['id' => "№", 'name' => "НАЗВА", 'price' => "ЦІНА", 'count' => "КІЛЬКІСТЬ", 'total_price' => "ВАРТІСТЬ"]);
|
||||||
|
$cart_columns = $this->count_columns($cart);
|
||||||
|
$this->print_lits($cart, $cart_columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
$total_price = array_reduce($cart, function ($carry, $item) {
|
||||||
|
return $carry + (int)$item['total_price'];
|
||||||
|
}, 0);
|
||||||
|
echo "РАЗОМ ДО СПЛАТИ: {$total_price}\n";
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
private function settings(): void
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
$name = readline("Ваше ім'я: ");
|
||||||
|
if ($name !== "" && preg_match("/[a-zA-Z]+/", $name))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$age = readline("Ваш вік: ");
|
||||||
|
|
||||||
|
if (!filter_var($age, FILTER_VALIDATE_INT)) {
|
||||||
|
echo "ПОМИЛКА! Вік користувача потрібно вказати числом\n\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($age < 7 || $age > 150) {
|
||||||
|
echo "ПОМИЛКА! Користувач повинен мати вік від 7 та до 150 років\n\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
$this->db->update_user($name, $age);
|
||||||
|
|
||||||
|
$this->state = State::Menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<array> $items
|
||||||
|
* @return array<int>
|
||||||
|
*/
|
||||||
|
private function count_columns($items): array
|
||||||
|
{
|
||||||
|
$columns = [];
|
||||||
|
foreach ($items as $item) {
|
||||||
|
foreach ($item as $field => $value) {
|
||||||
|
if (!key_exists($field, $columns))
|
||||||
|
$columns[$field] = mb_strlen($value);
|
||||||
|
else
|
||||||
|
$columns[$field] = max(mb_strlen($value), $columns[$field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param array $element
|
||||||
|
* @param array<int> $columns
|
||||||
|
*/
|
||||||
|
private function pad_row($element, $columns): string
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
foreach ($element as $field => $value)
|
||||||
|
$result[] = mb_str_pad($value, $columns[$field], ' ', STR_PAD_RIGHT);
|
||||||
|
|
||||||
|
return implode(" ", $result);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param array<array> $items
|
||||||
|
* @param array<int> $columns
|
||||||
|
*/
|
||||||
|
private function print_lits($items, $columns): void
|
||||||
|
{
|
||||||
|
foreach ($items as $item)
|
||||||
|
echo $this->pad_row($item, $columns) . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app = new App("db.sqlite");
|
||||||
|
} catch (AppException $e) {
|
||||||
|
echo $e;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$app->poll();
|
||||||
Binary file not shown.
@@ -0,0 +1,785 @@
|
|||||||
|
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
|
||||||
|
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Кафедра Програмної інженерії
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Звіт
|
||||||
|
з лабораторної роботи №3
|
||||||
|
з дисципліни: «Скриптові мови програмування»
|
||||||
|
з теми: « Створення WEB-застосунків за допомогою PHP «
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Виконав: Перевірив:
|
||||||
|
ст. гр. ПЗПІ-23-2 Старший викладач кафедри ПІ
|
||||||
|
Ситник Є. С. Сокорчук І. П.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Харків – 2025
|
||||||
|
2
|
||||||
|
3 СТВОРЕННЯ WEB-ЗАСТОСУНКІВ ЗА ДОПОМОГОЮ PHP
|
||||||
|
3.1 Історія змін
|
||||||
|
|
||||||
|
№ Дата Версія звіту Опис змін та виправлень
|
||||||
|
1 03.06.2025 0.1 Створено звіт
|
||||||
|
|
||||||
|
3.2 Мета роботи
|
||||||
|
|
||||||
|
Лабораторна робота полягає у розробці веб-застосунку «Продовольчий
|
||||||
|
магазин Весна» засобами мови програмування PHP з використанням HTML, CSS та
|
||||||
|
JavaScript для створення інтерактивного інтерфейсу користувача.
|
||||||
|
|
||||||
|
3.3 Хід роботи
|
||||||
|
3.3.1 Архітектура веб-застосунку
|
||||||
|
|
||||||
|
Для реалізації веб-застосунку було обрано модульний підхід з розділенням
|
||||||
|
логіки на окремі файли:
|
||||||
|
|
||||||
|
3.3.1.1 Структура файлів:
|
||||||
|
|
||||||
|
– index.php – головна сторінка застосунку з логотипом магазину;
|
||||||
|
– items.php – сторінка каталогу товарів з можливістю додавання до кошика;
|
||||||
|
– cart.php – сторінка кошика з переглядом обраних товарів та управлінням;
|
||||||
|
– handle_cart.php – обробник AJAX-запитів для роботи з кошиком;
|
||||||
|
– DB.php – клас для роботи з базою даних SQLite;
|
||||||
|
– style.css – стилі для оформлення веб-сторінок.
|
||||||
|
|
||||||
|
3.3.1.2 Основні компоненти:
|
||||||
|
|
||||||
|
– Клас DB – забезпечує взаємодію з базою даних SQLite, включаючи
|
||||||
|
методи для роботи з товарами, кошиком та налаштуваннями користувача;
|
||||||
|
– Клас DbException – спеціалізований виняток для обробки помилок бази
|
||||||
|
даних;
|
||||||
|
– Сесійна система – для збереження стану користувача між сторінками.
|
||||||
|
3
|
||||||
|
3.3.2 Структура бази даних
|
||||||
|
|
||||||
|
Веб-застосунок використовує базу даних SQLite з трьома основними
|
||||||
|
таблицями:
|
||||||
|
|
||||||
|
3.3.2.1 Таблиця settings:
|
||||||
|
|
||||||
|
– name (TEXT) – ім’я користувача;
|
||||||
|
– age (TEXT) – вік користувача.
|
||||||
|
|
||||||
|
3.3.2.2 Таблиця items:
|
||||||
|
|
||||||
|
– id (INTEGER PRIMARY KEY) – унікальний ідентифікатор товару;
|
||||||
|
– name (TEXT NOT NULL) – назва товару;
|
||||||
|
– price (REAL NOT NULL) – ціна товару.
|
||||||
|
|
||||||
|
3.3.2.3 Таблиця cart:
|
||||||
|
|
||||||
|
– id (INTEGER NOT NULL UNIQUE) – ідентифікатор товару;
|
||||||
|
– count (INTEGER NOT NULL) – кількість товару в кошику;
|
||||||
|
– FOREIGN KEY – зв’язок з таблицею items.
|
||||||
|
|
||||||
|
3.3.3 Функціональні можливості
|
||||||
|
|
||||||
|
3.3.3.1 Головна сторінка (index.php):
|
||||||
|
|
||||||
|
– Відображення логотипу магазину;
|
||||||
|
– Навігаційне меню з кількістю товарів у кошику;
|
||||||
|
– Привітання користувача за ім’ям;
|
||||||
|
– Підвал з інформацією про магазин.
|
||||||
|
|
||||||
|
3.3.3.2 Каталог товарів (items.php):
|
||||||
|
|
||||||
|
– Відображення списку доступних товарів з цінами;
|
||||||
|
– Форма для вибору кількості товару;
|
||||||
|
– Можливість додавання товарів до кошика;
|
||||||
|
– Валідація кількості товару (від 0 до 100 одиниць);
|
||||||
|
4
|
||||||
|
– Автоматичне оновлення лічильника кошика.
|
||||||
|
|
||||||
|
3.3.3.3 Кошик покупок (cart.php):
|
||||||
|
|
||||||
|
– Перегляд обраних товарів з детальною інформацією;
|
||||||
|
– Відображення кількості, ціни за одиницю та загальної вартості;
|
||||||
|
– Можливість видалення окремих товарів з кошика;
|
||||||
|
– Функція повного очищення кошика;
|
||||||
|
– Підрахунок загальної суми покупки;
|
||||||
|
– Кнопка «Сплатити» для завершення покупки.
|
||||||
|
|
||||||
|
3.3.3.4 Обробка запитів (handle_cart.php):
|
||||||
|
|
||||||
|
– Обробка POST-запитів для додавання товарів до кошика;
|
||||||
|
– Обробка DELETE-запитів для видалення товарів;
|
||||||
|
– Підтримка AJAX для асинхронного оновлення кошика;
|
||||||
|
– Валідація вхідних даних та обробка помилок.
|
||||||
|
|
||||||
|
3.3.4 Технічні особливості
|
||||||
|
|
||||||
|
3.3.4.1 Безпека:
|
||||||
|
|
||||||
|
– Використання підготовлених запитів (prepared statements) для запобігання
|
||||||
|
SQL-ін’єкціям;
|
||||||
|
– Функція «htmlspecialchars()» для запобігання XSS-атакам;
|
||||||
|
– Валідація та фільтрація користувацьких даних через «filter_input()»;
|
||||||
|
– Обробка винятків для коректної роботи з помилками.
|
||||||
|
|
||||||
|
3.3.4.2 Користувацький інтерфейс:
|
||||||
|
|
||||||
|
– Адаптивний дизайн з використанням CSS;
|
||||||
|
– Інтуїтивна навігація між сторінками;
|
||||||
|
– Зручні форми для взаємодії з користувачем.
|
||||||
|
5
|
||||||
|
3.3.4.3 База даних:
|
||||||
|
|
||||||
|
– Автоматичне створення таблиць при першому запуску;
|
||||||
|
– Заповнення початковими даними;
|
||||||
|
|
||||||
|
3.3.5 Методи класу DB
|
||||||
|
|
||||||
|
Клас DB містить наступні основні методи:
|
||||||
|
– retrieve_user() – отримання інформації про користувача;
|
||||||
|
– update_user() – оновлення профілю користувача;
|
||||||
|
– get_items() – отримання списку всіх товарів;
|
||||||
|
– get_item_by_id() – отримання конкретного товару за ID;
|
||||||
|
– get_cart() – отримання товарів у кошику з детальною інформацією;
|
||||||
|
– get_cart_count() – підрахунок загальної кількості товарів у кошику;
|
||||||
|
– get_cart_total() – підрахунок загальної вартості кошика;
|
||||||
|
– add_to_cart() – додавання товару до кошика або оновлення кількості;
|
||||||
|
– remove_from_cart() – видалення товару з кошика;
|
||||||
|
– empty_cart() – повне очищення кошика.
|
||||||
|
|
||||||
|
3.4 Висновки
|
||||||
|
|
||||||
|
Під час виконання даної лабораторної роботи було успішно розроблено веб-
|
||||||
|
застосунок інтернет-магазину з використанням сучасних веб-технологій. Зокрема,
|
||||||
|
було освоєно:
|
||||||
|
– створення багатосторінкових веб-застосунків на PHP;
|
||||||
|
– роботу з базами даних SQLite через PDO з дотриманням принципів
|
||||||
|
безпеки;
|
||||||
|
– реалізацію сесійної системи для збереження стану користувача;
|
||||||
|
– створення інтерактивного користувацького інтерфейсу з використанням
|
||||||
|
HTML та CSS;
|
||||||
|
– валідацію та фільтрацію користувацьких даних.
|
||||||
|
Застосунок демонструє практичне застосування технологій веб-розробки для
|
||||||
|
створення реальних рішень.
|
||||||
|
6
|
||||||
|
ДОДАТОК А
|
||||||
|
Відеозапис
|
||||||
|
|
||||||
|
Відеозапис презентації результатів лабораторної роботи: https://youtu.be/Gils7poMkgk
|
||||||
|
Хронологічний опис відеозапису:
|
||||||
|
00:00 – Вступ та загальний опис роботи
|
||||||
|
00:30 – Структура веб-застосунку
|
||||||
|
01:14 – Робота з базою даних та сесіями
|
||||||
|
01:43 – Обробка запитів користувача
|
||||||
|
03:56 – Демонстрація роботи веб-застосунку
|
||||||
|
7
|
||||||
|
ДОДАТОК Б
|
||||||
|
Програмний код
|
||||||
|
|
||||||
|
Б.1 Головна сторінка (index.php)
|
||||||
|
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/index.php
|
||||||
|
1 <?php
|
||||||
|
2 session_start();
|
||||||
|
3 require_once 'DB.php';
|
||||||
|
4
|
||||||
|
5 $db = new DB('shop.db');
|
||||||
|
6
|
||||||
|
7 if (!isset($_SESSION['user'])) {
|
||||||
|
8 $_SESSION['user'] = $db->retrieve_user();
|
||||||
|
9 }
|
||||||
|
10
|
||||||
|
11 $cart_count = $db->get_cart_count();
|
||||||
|
12 ?>
|
||||||
|
13
|
||||||
|
14 <!DOCTYPE html>
|
||||||
|
15 <html lang="uk">
|
||||||
|
16 <head>
|
||||||
|
17 <meta charset="UTF-8">
|
||||||
|
18 <title>Головна сторінка</title>
|
||||||
|
19 <link rel="stylesheet" href="style.css">
|
||||||
|
20 </head>
|
||||||
|
21 <body>
|
||||||
|
22 <header>
|
||||||
|
23 <h1>Продовольчий магазин "Весна"</h1>
|
||||||
|
24 <h3> Добрий день <?php echo $_SESSION['user']['name'] ?> </h3>
|
||||||
|
25 <nav>
|
||||||
|
26 <a href="index.php">Головна</a>
|
||||||
|
27 |
|
||||||
|
28 <a href="items.php">Товари</a>
|
||||||
|
29 |
|
||||||
|
30 <a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?
|
||||||
|
>)</a>
|
||||||
|
31 </nav>
|
||||||
|
32 </header>
|
||||||
|
33
|
||||||
|
34 <div class="container" style="display: flex; flex-direction:
|
||||||
|
column; align-items: center;">
|
||||||
|
35 <img src="logo.png" alt="logo" style="width: 90%;">
|
||||||
|
36 </div>
|
||||||
|
37
|
||||||
|
38 <footer>
|
||||||
|
39 <nav>
|
||||||
|
40 <a href="index.php">Головна</a>
|
||||||
|
41 |
|
||||||
|
42 <a href="items.php">Товари</a>
|
||||||
|
43 |
|
||||||
|
8
|
||||||
|
|
||||||
|
44 <a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?
|
||||||
|
>)</a>
|
||||||
|
45 </nav>
|
||||||
|
46 <p>© <?php echo date("Y"); ?> ТОВ "Весна". Усі права
|
||||||
|
захищені.</p>
|
||||||
|
47 </footer>
|
||||||
|
48 </body>
|
||||||
|
49 </html>
|
||||||
|
|
||||||
|
|
||||||
|
Б.2 Каталог товарів (items.php)
|
||||||
|
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/items.php
|
||||||
|
1 <?php
|
||||||
|
2 session_start();
|
||||||
|
3 require_once 'DB.php';
|
||||||
|
4
|
||||||
|
5 $db = new DB('shop.db');
|
||||||
|
6
|
||||||
|
7 if (!isset($_SESSION['user'])) {
|
||||||
|
8 $_SESSION['user'] = $db->retrieve_user();
|
||||||
|
9 }
|
||||||
|
10
|
||||||
|
11 $items = $db->get_items();
|
||||||
|
12 $cart_count = $db->get_cart_count();
|
||||||
|
13 ?>
|
||||||
|
14
|
||||||
|
15 <!DOCTYPE html>
|
||||||
|
16 <html lang="uk">
|
||||||
|
17 <head>
|
||||||
|
18 <meta charset="UTF-8">
|
||||||
|
19 <title>Сторінка товарів</title>
|
||||||
|
20 <link rel="stylesheet" href="style.css">
|
||||||
|
21 </head>
|
||||||
|
22 <body>
|
||||||
|
23 <header>
|
||||||
|
24 <h1>Продовольчий магазин "Весна"</h1>
|
||||||
|
25 <h3> Добрий день <?php echo $_SESSION['user']['name'] ?> </h3>
|
||||||
|
26 <nav>
|
||||||
|
27 <a href="index.php">Головна</a>
|
||||||
|
28 |
|
||||||
|
29 <a href="items.php">Товари</a>
|
||||||
|
30 |
|
||||||
|
31 <a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?
|
||||||
|
>)</a>
|
||||||
|
32 </nav>
|
||||||
|
33 </header>
|
||||||
|
34
|
||||||
|
35 <div class="container">
|
||||||
|
36 <h2>Доступні товари</h2>
|
||||||
|
37 <div class="product-list">
|
||||||
|
38 <?php foreach ($items as $item): ?>
|
||||||
|
39 <div>
|
||||||
|
9
|
||||||
|
|
||||||
|
40 <h2><?php echo htmlspecialchars($item['name']); ?
|
||||||
|
></h2>
|
||||||
|
41 <h3>Ціна: <?php echo number_format($item['price'],
|
||||||
|
2); ?> грн</h3>
|
||||||
|
42
|
||||||
|
43 <form action="handle_cart.php" method="POST">
|
||||||
|
44 <input type="hidden" name="product_id"
|
||||||
|
value="<?php echo htmlspecialchars($item['id']); ?>">
|
||||||
|
45 <label for="quantity_<?php echo
|
||||||
|
htmlspecialchars($item['id']); ?>">Кількість:</label>
|
||||||
|
46 <input type="number" id="quantity_<?php echo
|
||||||
|
htmlspecialchars($item['id']); ?>" name="quantity" value="0" min="0"
|
||||||
|
max="100">
|
||||||
|
47 <button type="submit">Купити</button>
|
||||||
|
48 </form>
|
||||||
|
49 </div>
|
||||||
|
50 <?php endforeach; ?>
|
||||||
|
51 </div>
|
||||||
|
52 </div>
|
||||||
|
53
|
||||||
|
54 <footer>
|
||||||
|
55 <nav>
|
||||||
|
56 <a href="index.php">Головна</a>
|
||||||
|
57 |
|
||||||
|
58 <a href="items.php">Товари</a>
|
||||||
|
59 |
|
||||||
|
60 <a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?
|
||||||
|
>)</a>
|
||||||
|
61 </nav>
|
||||||
|
62 <p>© <?php echo date("Y"); ?> ТОВ "Весна". Усі права
|
||||||
|
захищені.</p>
|
||||||
|
63 </footer>
|
||||||
|
64 </body>
|
||||||
|
65 </html>
|
||||||
|
|
||||||
|
|
||||||
|
Б.3 Кошик покупок (cart.php)
|
||||||
|
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/cart.php
|
||||||
|
1 <?php
|
||||||
|
2 session_start();
|
||||||
|
3 require_once 'DB.php';
|
||||||
|
4
|
||||||
|
5 $db = new DB("shop.db");
|
||||||
|
6
|
||||||
|
7 if (!isset($_SESSION['user'])) {
|
||||||
|
8 $_SESSION['user'] = $db->retrieve_user();
|
||||||
|
9 }
|
||||||
|
10
|
||||||
|
11 $cart_items = $db->get_cart();
|
||||||
|
12 $cart_total = $db->get_cart_total();
|
||||||
|
13 $cart_count = $db->get_cart_count();
|
||||||
|
14 ?>
|
||||||
|
10
|
||||||
|
|
||||||
|
15
|
||||||
|
16 <!DOCTYPE html>
|
||||||
|
17 <html lang="uk">
|
||||||
|
18 <head>
|
||||||
|
19 <meta charset="UTF-8">
|
||||||
|
20 <title>Кошик</title>
|
||||||
|
21 <link rel="stylesheet" href="style.css">
|
||||||
|
22 </head>
|
||||||
|
23 <body>
|
||||||
|
24 <header>
|
||||||
|
25 <h1>Продовольчий магазин "Весна"</h1>
|
||||||
|
26 <h3> Добрий день <?php echo $_SESSION['user']['name'] ?> </h3>
|
||||||
|
27 <nav>
|
||||||
|
28 <a href="index.php">Головна</a>
|
||||||
|
29 |
|
||||||
|
30 <a href="items.php">Товари</a>
|
||||||
|
31 |
|
||||||
|
32 <a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?
|
||||||
|
>)</a>
|
||||||
|
33 </nav>
|
||||||
|
34 </header>
|
||||||
|
35
|
||||||
|
36 <div class="container">
|
||||||
|
37 <?php if (empty($cart_items)): ?>
|
||||||
|
38 <div style="display: flex; align-items: center; justify-
|
||||||
|
content: space-evenly;">
|
||||||
|
39 <h3>Ваш кошик порожній <a href="items.php">Перейти до
|
||||||
|
покупок</a> </h3>
|
||||||
|
40 </div>
|
||||||
|
41 <?php else: ?>
|
||||||
|
42 <div style="display: flex; align-items: center; justify-
|
||||||
|
content: space-evenly;">
|
||||||
|
43 <h3>Ваш кошик</h3>
|
||||||
|
44 <h3 class="cart-summary">
|
||||||
|
45 Загальна сума: <?php echo
|
||||||
|
number_format($cart_total, 2); ?> грн
|
||||||
|
46 </h3>
|
||||||
|
47
|
||||||
|
48 <button type="submit">Сплатити</button>
|
||||||
|
49
|
||||||
|
50 <button onclick="fetch('handle_cart.php', {'method':
|
||||||
|
'DELETE'}).then(_ => { location.reload(); });">Очистити</button>
|
||||||
|
51 </div>
|
||||||
|
52
|
||||||
|
53 <div class="product-list">
|
||||||
|
54 <?php foreach ($cart_items as $item): ?>
|
||||||
|
55 <div>
|
||||||
|
56 <h2>
|
||||||
|
57 <?php echo
|
||||||
|
htmlspecialchars($item['name']); ?>
|
||||||
|
58 <br>
|
||||||
|
59 <?php echo
|
||||||
|
htmlspecialchars($item['count']); ?> шт.
|
||||||
|
60 </h2>
|
||||||
|
61
|
||||||
|
11
|
||||||
|
|
||||||
|
62 <span>Ціна за одиницю: <?php echo
|
||||||
|
number_format($item['price'], 2); ?> грн</span>
|
||||||
|
63 <br>
|
||||||
|
64 <span>Загальна ціна: <?php echo
|
||||||
|
number_format($item['total_price'], 2); ?> грн</span>
|
||||||
|
65
|
||||||
|
66 <br><br>
|
||||||
|
67
|
||||||
|
68 <button
|
||||||
|
69 style="width: 100%;"
|
||||||
|
70 onclick="
|
||||||
|
71 fetch(
|
||||||
|
72 'handle_cart.php?product_id=<?php echo
|
||||||
|
htmlspecialchars($item['id']); ?>',
|
||||||
|
73 { 'method': 'DELETE' }
|
||||||
|
74 ).then(_ => { location.reload(); });">
|
||||||
|
75 Видалити
|
||||||
|
76 </button>
|
||||||
|
77 </div>
|
||||||
|
78 <?php endforeach; ?>
|
||||||
|
79 </div>
|
||||||
|
80 <?php endif; ?>
|
||||||
|
81 </div>
|
||||||
|
82
|
||||||
|
83 <footer>
|
||||||
|
84 <nav>
|
||||||
|
85 <a href="index.php">Головна</a>
|
||||||
|
86 |
|
||||||
|
87 <a href="items.php">Товари</a>
|
||||||
|
88 |
|
||||||
|
89 <a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?
|
||||||
|
>)</a>
|
||||||
|
90 </nav>
|
||||||
|
91 <p>© <?php echo date("Y"); ?> ТОВ "Весна". Усі права
|
||||||
|
захищені.</p>
|
||||||
|
92 </footer>
|
||||||
|
93 </body>
|
||||||
|
94 </html>
|
||||||
|
|
||||||
|
|
||||||
|
Б.4 Обробник кошика (handle_cart.php)
|
||||||
|
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/handle_cart.php
|
||||||
|
1 <?php
|
||||||
|
2 session_start();
|
||||||
|
3 require_once 'DB.php';
|
||||||
|
4
|
||||||
|
5 $db_path = 'shop.db';
|
||||||
|
6 $db = new DB($db_path);
|
||||||
|
7
|
||||||
|
8 switch ($_SERVER['REQUEST_METHOD']) {
|
||||||
|
9 case 'POST':
|
||||||
|
12
|
||||||
|
|
||||||
|
10 $product_id = filter_input(INPUT_POST, 'product_id',
|
||||||
|
FILTER_VALIDATE_INT);
|
||||||
|
11 $quantity = filter_input(INPUT_POST, 'quantity',
|
||||||
|
FILTER_VALIDATE_INT);
|
||||||
|
12
|
||||||
|
13 if ($product_id !== false && $product_id !== null &&
|
||||||
|
$quantity !== false && $quantity !== null) {
|
||||||
|
14 try {
|
||||||
|
15 $db->add_to_cart($product_id, $quantity);
|
||||||
|
16 } catch (DbException $e) {
|
||||||
|
17 error_log("Cart handling error: " . $e->getMessage());
|
||||||
|
18 }
|
||||||
|
19 }
|
||||||
|
20 header('Location: items.php');
|
||||||
|
21 break;
|
||||||
|
22
|
||||||
|
23 case 'DELETE':
|
||||||
|
24 $product_id = filter_input(INPUT_GET, 'product_id',
|
||||||
|
FILTER_VALIDATE_INT);
|
||||||
|
25
|
||||||
|
26 if ($product_id === null) {
|
||||||
|
27 try {
|
||||||
|
28 $db->empty_cart();
|
||||||
|
29 } catch (DbException $e) {
|
||||||
|
30 error_log("Cart handling error: " . $e->getMessage());
|
||||||
|
31 }
|
||||||
|
32 } else if ($product_id !== false) {
|
||||||
|
33 try {
|
||||||
|
34 $db->remove_from_cart($product_id);
|
||||||
|
35 } catch (DbException $e) {
|
||||||
|
36 error_log("Cart handling error: " . $e->getMessage());
|
||||||
|
37 }
|
||||||
|
38 }
|
||||||
|
39
|
||||||
|
40 header('Location: cart.php');
|
||||||
|
41 break;
|
||||||
|
42
|
||||||
|
43 default:
|
||||||
|
44 break;
|
||||||
|
45 }
|
||||||
|
46
|
||||||
|
47 exit();
|
||||||
|
|
||||||
|
|
||||||
|
Б.5 Клас для роботи з базою даних (DB.php)
|
||||||
|
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/DB.php
|
||||||
|
1 <?php
|
||||||
|
2
|
||||||
|
3 class DbException extends Exception {}
|
||||||
|
4
|
||||||
|
5 class DB
|
||||||
|
6 {
|
||||||
|
13
|
||||||
|
|
||||||
|
7 private $pdo;
|
||||||
|
8
|
||||||
|
9 /**
|
||||||
|
10 * Initializes database
|
||||||
|
11 *
|
||||||
|
12 * @param string $db_path
|
||||||
|
13 * @throws DbException If there's a database error.
|
||||||
|
14 */
|
||||||
|
15 public function __construct($db_path)
|
||||||
|
16 {
|
||||||
|
17 try {
|
||||||
|
18 $this->pdo = new PDO("sqlite:" . $db_path);
|
||||||
|
19 $this->pdo->setAttribute(PDO::ATTR_ERRMODE,
|
||||||
|
PDO::ERRMODE_EXCEPTION);
|
||||||
|
20 } catch (PDOException $e) {
|
||||||
|
21 throw new DbException("Connection to DB failed.\nCaused
|
||||||
|
by: " . $e->getMessage());
|
||||||
|
22 }
|
||||||
|
23
|
||||||
|
24 // Ініціалізація таблиць та початкових даних
|
||||||
|
25 try {
|
||||||
|
26 $this->pdo->exec("
|
||||||
|
27 CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
28 name TEXT,
|
||||||
|
29 age TEXT
|
||||||
|
30 );
|
||||||
|
31 ");
|
||||||
|
32 if ($this->pdo->query("SELECT COUNT(*) FROM settings;")-
|
||||||
|
>fetchColumn() == 0) {
|
||||||
|
33 $this->pdo->exec("INSERT INTO settings (name, age)
|
||||||
|
VALUES ('user', 0);");
|
||||||
|
34 }
|
||||||
|
35 } catch (PDOException $e) {
|
||||||
|
36 throw new DbException("Error initialising settings table.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
37 }
|
||||||
|
38
|
||||||
|
39 try {
|
||||||
|
40 $this->pdo->exec("
|
||||||
|
41 CREATE TABLE IF NOT EXISTS items (
|
||||||
|
42 id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
43 name TEXT NOT NULL,
|
||||||
|
44 price REAL NOT NULL
|
||||||
|
45 );
|
||||||
|
46 ");
|
||||||
|
47 if ($this->pdo->query("SELECT COUNT(id) FROM items;")-
|
||||||
|
>fetchColumn() == 0) {
|
||||||
|
48 $this->pdo->exec("
|
||||||
|
49 INSERT INTO items (name, price) VALUES ('Молоко
|
||||||
|
пастеризоване', 32.50);
|
||||||
|
50 INSERT INTO items (name, price) VALUES ('Хліб
|
||||||
|
чорний', 18.00);
|
||||||
|
51 INSERT INTO items (name, price) VALUES ('Сир
|
||||||
|
білий', 85.00);
|
||||||
|
52 INSERT INTO items (name, price) VALUES ('Сметана
|
||||||
|
20%', 45.80);
|
||||||
|
14
|
||||||
|
|
||||||
|
53 INSERT INTO items (name, price) VALUES ('Кефір
|
||||||
|
1%', 28.50);
|
||||||
|
54 INSERT INTO items (name, price) VALUES ('Вода
|
||||||
|
газована', 25.00);
|
||||||
|
55 INSERT INTO items (name, price) VALUES ('Печиво
|
||||||
|
\"Весна\"', 42.30);
|
||||||
|
56 INSERT INTO items (name, price) VALUES ('Масло
|
||||||
|
вершкове', 125.00);
|
||||||
|
57 INSERT INTO items (name, price) VALUES ('Йогурт
|
||||||
|
натуральний', 38.90);
|
||||||
|
58 INSERT INTO items (name, price) VALUES ('Сік
|
||||||
|
апельсиновий', 55.00);
|
||||||
|
59 ");
|
||||||
|
60 }
|
||||||
|
61 } catch (PDOException $e) {
|
||||||
|
62 throw new DbException("Error initialising items table.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
63 }
|
||||||
|
64
|
||||||
|
65 try {
|
||||||
|
66 $this->pdo->exec("
|
||||||
|
67 CREATE TABLE IF NOT EXISTS cart (
|
||||||
|
68 id INTEGER NOT NULL UNIQUE,
|
||||||
|
69 count INTEGER NOT NULL,
|
||||||
|
70 FOREIGN KEY(id) REFERENCES items(id) ON DELETE
|
||||||
|
CASCADE
|
||||||
|
71 );
|
||||||
|
72 ");
|
||||||
|
73 } catch (PDOException $e) {
|
||||||
|
74 throw new DbException("Error initialising cart table.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
75 }
|
||||||
|
76 }
|
||||||
|
77
|
||||||
|
78 /**
|
||||||
|
79 * Retrieve user information from the database
|
||||||
|
80 *
|
||||||
|
81 * @return array
|
||||||
|
82 * @throws DbException If there's a database error.
|
||||||
|
83 */
|
||||||
|
84 public function retrieve_user(): array
|
||||||
|
85 {
|
||||||
|
86 try {
|
||||||
|
87 $stmt = $this->pdo->query("SELECT name, age FROM
|
||||||
|
settings;");
|
||||||
|
88 return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
89 } catch (PDOException $e) {
|
||||||
|
90 throw new DbException("Error retrieving user info.\nCaused
|
||||||
|
by: " . $e->getMessage());
|
||||||
|
91 }
|
||||||
|
92 }
|
||||||
|
93
|
||||||
|
94 /**
|
||||||
|
95 * Fetches all items from the database.
|
||||||
|
96 *
|
||||||
|
97 * @return array[]
|
||||||
|
15
|
||||||
|
|
||||||
|
98 * @throws DbException If there's a database error.
|
||||||
|
99 */
|
||||||
|
100 public function get_items(): array
|
||||||
|
101 {
|
||||||
|
102 try {
|
||||||
|
103 $stmt = $this->pdo->query("SELECT id, name, price FROM
|
||||||
|
items ORDER BY id;");
|
||||||
|
104 return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
105 } catch (PDOException $e) {
|
||||||
|
106 throw new DbException("Error retrieving data from the
|
||||||
|
items table.\nCaused by: " . $e->getMessage());
|
||||||
|
107 }
|
||||||
|
108 }
|
||||||
|
109
|
||||||
|
110 /**
|
||||||
|
111 * Fetches all items in the cart from the database.
|
||||||
|
112 *
|
||||||
|
113 * @return array[]
|
||||||
|
114 * @throws DbException If there's a database error.
|
||||||
|
115 */
|
||||||
|
116 public function get_cart(): array
|
||||||
|
117 {
|
||||||
|
118 try {
|
||||||
|
119 $stmt = $this->pdo->query(
|
||||||
|
120 "SELECT
|
||||||
|
121 cart.id,
|
||||||
|
122 items.name,
|
||||||
|
123 items.price,
|
||||||
|
124 cart.count,
|
||||||
|
125 ROUND(items.price * cart.count, 2) as total_price
|
||||||
|
126 FROM cart
|
||||||
|
127 INNER JOIN items ON cart.id = items.id
|
||||||
|
128 ORDER BY cart.id;"
|
||||||
|
129 );
|
||||||
|
130 return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
131 } catch (PDOException $e) {
|
||||||
|
132 throw new DbException("Error retrieving cart items.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
133 }
|
||||||
|
134 }
|
||||||
|
135
|
||||||
|
136 /**
|
||||||
|
137 * Get total items count in cart
|
||||||
|
138 *
|
||||||
|
139 * @return int
|
||||||
|
140 * @throws DbException If there's a database error.
|
||||||
|
141 */
|
||||||
|
142 public function get_cart_count(): int
|
||||||
|
143 {
|
||||||
|
144 try {
|
||||||
|
145 $stmt = $this->pdo->query("SELECT COALESCE(SUM(count), 0)
|
||||||
|
FROM cart;");
|
||||||
|
146 return (int)$stmt->fetchColumn();
|
||||||
|
147 } catch (PDOException $e) {
|
||||||
|
148 throw new DbException("Error getting cart count.\nCaused
|
||||||
|
by: " . $e->getMessage());
|
||||||
|
16
|
||||||
|
|
||||||
|
149 }
|
||||||
|
150 }
|
||||||
|
151
|
||||||
|
152 /**
|
||||||
|
153 * Get total price of all items in cart
|
||||||
|
154 *
|
||||||
|
155 * @return float
|
||||||
|
156 * @throws DbException If there's a database error.
|
||||||
|
157 */
|
||||||
|
158 public function get_cart_total(): float
|
||||||
|
159 {
|
||||||
|
160 try {
|
||||||
|
161 $stmt = $this->pdo->query(
|
||||||
|
162 "SELECT COALESCE(SUM(items.price * cart.count), 0.0)
|
||||||
|
163 FROM cart
|
||||||
|
164 INNER JOIN items ON cart.id = items.id;"
|
||||||
|
165 );
|
||||||
|
166 return (float)$stmt->fetchColumn();
|
||||||
|
167 } catch (PDOException $e) {
|
||||||
|
168 throw new DbException("Error calculating cart total.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
169 }
|
||||||
|
170 }
|
||||||
|
171
|
||||||
|
172 /**
|
||||||
|
173 * Add item to the cart or update its quantity.
|
||||||
|
174 *
|
||||||
|
175 * @param int $id
|
||||||
|
176 * @param int $count
|
||||||
|
177 *
|
||||||
|
178 * @return bool
|
||||||
|
179 * @throws DbException If there's a database error or item doesn't
|
||||||
|
exist.
|
||||||
|
180 */
|
||||||
|
181 public function add_to_cart($id, $count): bool
|
||||||
|
182 {
|
||||||
|
183 try {
|
||||||
|
184 // Check if the item exists
|
||||||
|
185 $item = $this->get_item_by_id($id);
|
||||||
|
186 if (!$item) {
|
||||||
|
187 throw new DbException("Item with ID $id does not
|
||||||
|
exist.");
|
||||||
|
188 }
|
||||||
|
189
|
||||||
|
190 // If count is 0 or less, remove the item from the cart
|
||||||
|
191 if ($count <= 0) {
|
||||||
|
192 return $this->remove_from_cart($id);
|
||||||
|
193 }
|
||||||
|
194
|
||||||
|
195 // Insert or update the cart item
|
||||||
|
196 $stmt = $this->pdo->prepare(
|
||||||
|
197 "INSERT INTO cart (id, count)
|
||||||
|
198 VALUES (:id, :count)
|
||||||
|
199 ON CONFLICT(id) DO UPDATE SET
|
||||||
|
200 count = excluded.count;"
|
||||||
|
201 );
|
||||||
|
17
|
||||||
|
|
||||||
|
202 return $stmt->execute(['id' => $id, 'count' => $count]);
|
||||||
|
203 } catch (PDOException $e) {
|
||||||
|
204 throw new DbException("Error adding/updating item in the
|
||||||
|
cart.\nCaused by: " . $e->getMessage());
|
||||||
|
205 }
|
||||||
|
206 }
|
||||||
|
207
|
||||||
|
208 /**
|
||||||
|
209 * Empty the cart
|
||||||
|
210 *
|
||||||
|
211 * @return bool
|
||||||
|
212 * @throws DbException If there's a database error.
|
||||||
|
213 */
|
||||||
|
214 public function empty_cart(): bool
|
||||||
|
215 {
|
||||||
|
216 try {
|
||||||
|
217 $stmt = $this->pdo->prepare("DELETE FROM cart");
|
||||||
|
218 return $stmt->execute();
|
||||||
|
219 } catch (PDOException $e) {
|
||||||
|
220 throw new DbException("Error removing item from the cart.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
221 }
|
||||||
|
222 }
|
||||||
|
223
|
||||||
|
224 /**
|
||||||
|
225 * Remove an item from the cart
|
||||||
|
226 *
|
||||||
|
227 * @param int $id
|
||||||
|
228 *
|
||||||
|
229 * @return bool
|
||||||
|
230 * @throws DbException If there's a database error.
|
||||||
|
231 */
|
||||||
|
232 public function remove_from_cart($id): bool
|
||||||
|
233 {
|
||||||
|
234 try {
|
||||||
|
235 $stmt = $this->pdo->prepare("DELETE FROM cart WHERE id
|
||||||
|
= :id");
|
||||||
|
236 return $stmt->execute(['id' => $id]);
|
||||||
|
237 } catch (PDOException $e) {
|
||||||
|
238 throw new DbException("Error removing item from the cart.
|
||||||
|
\nCaused by: " . $e->getMessage());
|
||||||
|
239 }
|
||||||
|
240 }
|
||||||
|
241 }
|
||||||
|
|
||||||
@@ -0,0 +1,712 @@
|
|||||||
|
#import "@local/nure:0.1.0": *
|
||||||
|
|
||||||
|
#show: pz-lb.with(
|
||||||
|
title: [Створення WEB-застосунків за допомогою PHP],
|
||||||
|
subject: "СМП",
|
||||||
|
doctype: "ЛБ",
|
||||||
|
worknumber: 3,
|
||||||
|
edu_program: "ПЗПІ",
|
||||||
|
university: "ХНУРЕ",
|
||||||
|
mentors: (
|
||||||
|
(
|
||||||
|
name: "Сокорчук І. П.",
|
||||||
|
degree: "Старший викладач кафедри ПІ",
|
||||||
|
gender: "m",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
authors: (
|
||||||
|
(
|
||||||
|
name: "Ситник Є. С.",
|
||||||
|
course: 2,
|
||||||
|
edu: "ПЗПІ",
|
||||||
|
gender: "m",
|
||||||
|
group: "23-2",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
#v(-spacing)
|
||||||
|
== Історія змін
|
||||||
|
#figure(
|
||||||
|
table(
|
||||||
|
align: left,
|
||||||
|
columns: (auto, 1fr, 1fr, 3fr),
|
||||||
|
table.header([№], [Дата], [Версія звіту], [Опис змін та виправлень]),
|
||||||
|
[1], [03.06.2025], [0.1], [Створено звіт],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
== Мета роботи
|
||||||
|
Лабораторна робота полягає у розробці веб-застосунку "Продовольчий магазин Весна" засобами мови програмування PHP з використанням HTML, CSS та JavaScript для створення інтерактивного інтерфейсу користувача.
|
||||||
|
|
||||||
|
== Хід роботи
|
||||||
|
#v(-spacing)
|
||||||
|
=== Архітектура веб-застосунку
|
||||||
|
|
||||||
|
Для реалізації веб-застосунку було обрано модульний підхід з розділенням логіки на окремі файли:
|
||||||
|
|
||||||
|
==== Структура файлів:
|
||||||
|
|
||||||
|
- *index.php* -- головна сторінка застосунку з логотипом магазину;
|
||||||
|
- *items.php* -- сторінка каталогу товарів з можливістю додавання до кошика;
|
||||||
|
- *cart.php* -- сторінка кошика з переглядом обраних товарів та управлінням;
|
||||||
|
- *handle_cart.php* -- обробник AJAX-запитів для роботи з кошиком;
|
||||||
|
- *DB.php* -- клас для роботи з базою даних SQLite;
|
||||||
|
- *style.css* -- стилі для оформлення веб-сторінок.
|
||||||
|
|
||||||
|
==== Основні компоненти:
|
||||||
|
|
||||||
|
- *Клас DB* -- забезпечує взаємодію з базою даних SQLite, включаючи методи для роботи з товарами, кошиком та налаштуваннями користувача;
|
||||||
|
- *Клас DbException* -- спеціалізований виняток для обробки помилок бази даних;
|
||||||
|
- *Сесійна система* -- для збереження стану користувача між сторінками.
|
||||||
|
|
||||||
|
=== Структура бази даних
|
||||||
|
|
||||||
|
Веб-застосунок використовує базу даних SQLite з трьома основними таблицями:
|
||||||
|
|
||||||
|
==== Таблиця settings:
|
||||||
|
- *name* (TEXT) -- ім'я користувача;
|
||||||
|
- *age* (TEXT) -- вік користувача.
|
||||||
|
|
||||||
|
==== Таблиця items:
|
||||||
|
- *id* (INTEGER PRIMARY KEY) -- унікальний ідентифікатор товару;
|
||||||
|
- *name* (TEXT NOT NULL) -- назва товару;
|
||||||
|
- *price* (REAL NOT NULL) -- ціна товару.
|
||||||
|
|
||||||
|
==== Таблиця cart:
|
||||||
|
- *id* (INTEGER NOT NULL UNIQUE) -- ідентифікатор товару;
|
||||||
|
- *count* (INTEGER NOT NULL) -- кількість товару в кошику;
|
||||||
|
- *FOREIGN KEY* -- зв'язок з таблицею items.
|
||||||
|
|
||||||
|
=== Функціональні можливості
|
||||||
|
|
||||||
|
==== Головна сторінка (index.php):
|
||||||
|
- Відображення логотипу магазину;
|
||||||
|
- Навігаційне меню з кількістю товарів у кошику;
|
||||||
|
- Привітання користувача за ім'ям;
|
||||||
|
- Підвал з інформацією про магазин.
|
||||||
|
|
||||||
|
==== Каталог товарів (items.php):
|
||||||
|
- Відображення списку доступних товарів з цінами;
|
||||||
|
- Форма для вибору кількості товару;
|
||||||
|
- Можливість додавання товарів до кошика;
|
||||||
|
- Валідація кількості товару (від 0 до 100 одиниць);
|
||||||
|
- Автоматичне оновлення лічильника кошика.
|
||||||
|
|
||||||
|
==== Кошик покупок (cart.php):
|
||||||
|
- Перегляд обраних товарів з детальною інформацією;
|
||||||
|
- Відображення кількості, ціни за одиницю та загальної вартості;
|
||||||
|
- Можливість видалення окремих товарів з кошика;
|
||||||
|
- Функція повного очищення кошика;
|
||||||
|
- Підрахунок загальної суми покупки;
|
||||||
|
- Кнопка "Сплатити" для завершення покупки.
|
||||||
|
|
||||||
|
==== Обробка запитів (handle_cart.php):
|
||||||
|
- Обробка POST-запитів для додавання товарів до кошика;
|
||||||
|
- Обробка DELETE-запитів для видалення товарів;
|
||||||
|
- Підтримка AJAX для асинхронного оновлення кошика;
|
||||||
|
- Валідація вхідних даних та обробка помилок.
|
||||||
|
|
||||||
|
=== Технічні особливості
|
||||||
|
|
||||||
|
==== Безпека:
|
||||||
|
- Використання підготовлених запитів (prepared statements) для запобігання SQL-ін'єкціям;
|
||||||
|
- Функція "htmlspecialchars()" для запобігання XSS-атакам;
|
||||||
|
- Валідація та фільтрація користувацьких даних через "filter_input()";
|
||||||
|
- Обробка винятків для коректної роботи з помилками.
|
||||||
|
|
||||||
|
==== Користувацький інтерфейс:
|
||||||
|
- Адаптивний дизайн з використанням CSS;
|
||||||
|
- Інтуїтивна навігація між сторінками;
|
||||||
|
- Зручні форми для взаємодії з користувачем.
|
||||||
|
|
||||||
|
==== База даних:
|
||||||
|
- Автоматичне створення таблиць при першому запуску;
|
||||||
|
- Заповнення початковими даними;
|
||||||
|
|
||||||
|
=== Методи класу DB
|
||||||
|
|
||||||
|
Клас DB містить наступні основні методи:
|
||||||
|
|
||||||
|
- *retrieve_user()* -- отримання інформації про користувача;
|
||||||
|
- *update_user()* -- оновлення профілю користувача;
|
||||||
|
- *get_items()* -- отримання списку всіх товарів;
|
||||||
|
- *get_item_by_id()* -- отримання конкретного товару за ID;
|
||||||
|
- *get_cart()* -- отримання товарів у кошику з детальною інформацією;
|
||||||
|
- *get_cart_count()* -- підрахунок загальної кількості товарів у кошику;
|
||||||
|
- *get_cart_total()* -- підрахунок загальної вартості кошика;
|
||||||
|
- *add_to_cart()* -- додавання товару до кошика або оновлення кількості;
|
||||||
|
- *remove_from_cart()* -- видалення товару з кошика;
|
||||||
|
- *empty_cart()* -- повне очищення кошика.
|
||||||
|
|
||||||
|
== Висновки
|
||||||
|
|
||||||
|
Під час виконання даної лабораторної роботи було успішно розроблено веб-застосунок інтернет-магазину з використанням сучасних веб-технологій. Зокрема, було освоєно:
|
||||||
|
|
||||||
|
- створення багатосторінкових веб-застосунків на PHP;
|
||||||
|
- роботу з базами даних SQLite через PDO з дотриманням принципів безпеки;
|
||||||
|
- реалізацію сесійної системи для збереження стану користувача;
|
||||||
|
- створення інтерактивного користувацького інтерфейсу з використанням HTML та CSS;
|
||||||
|
- валідацію та фільтрацію користувацьких даних.
|
||||||
|
|
||||||
|
Застосунок демонструє практичне застосування технологій веб-розробки для створення реальних рішень.
|
||||||
|
|
||||||
|
#set raw(tab-size: 8)
|
||||||
|
#show raw.where(block: true): code => {
|
||||||
|
set text(11pt, top-edge: 1em, bottom-edge: 0em)
|
||||||
|
set par(leading: 0.17em)
|
||||||
|
|
||||||
|
grid(
|
||||||
|
columns: (auto, auto),
|
||||||
|
column-gutter: 1em,
|
||||||
|
row-gutter: 0.17em,
|
||||||
|
align: (right, raw.align),
|
||||||
|
..for line in code.lines {
|
||||||
|
(
|
||||||
|
text(fill: gray)[#line.number],
|
||||||
|
{
|
||||||
|
set text(weight: "semibold")
|
||||||
|
line.body
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#show: appendices_style
|
||||||
|
= Відеозапис
|
||||||
|
Відеозапис презентації результатів лабораторної роботи: https://youtu.be/Gils7poMkgk
|
||||||
|
|
||||||
|
*Хронологічний опис відеозапису:*
|
||||||
|
|
||||||
|
00:00 -- Вступ та загальний опис роботи
|
||||||
|
|
||||||
|
00:30 -- Структура веб-застосунку
|
||||||
|
|
||||||
|
01:14 -- Робота з базою даних та сесіями
|
||||||
|
|
||||||
|
01:43 -- Обробка запитів користувача
|
||||||
|
|
||||||
|
03:56 -- Демонстрація роботи веб-застосунку
|
||||||
|
|
||||||
|
= Програмний код
|
||||||
|
|
||||||
|
== Головна сторінка (index.php)
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/index.php
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'DB.php';
|
||||||
|
|
||||||
|
$db = new DB('shop.db');
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
$_SESSION['user'] = $db->retrieve_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart_count = $db->get_cart_count();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="uk">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Головна сторінка</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Продовольчий магазин "Весна"</h1>
|
||||||
|
<h3> Добрий день <?php echo $_SESSION['user']['name'] ?> </h3>
|
||||||
|
<nav>
|
||||||
|
<a href="index.php">Головна</a>
|
||||||
|
|
|
||||||
|
<a href="items.php">Товари</a>
|
||||||
|
|
|
||||||
|
<a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?>)</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container" style="display: flex; flex-direction: column; align-items: center;">
|
||||||
|
<img src="logo.png" alt="logo" style="width: 90%;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<nav>
|
||||||
|
<a href="index.php">Головна</a>
|
||||||
|
|
|
||||||
|
<a href="items.php">Товари</a>
|
||||||
|
|
|
||||||
|
<a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?>)</a>
|
||||||
|
</nav>
|
||||||
|
<p>© <?php echo date("Y"); ?> ТОВ "Весна". Усі права захищені.</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
== Каталог товарів (items.php)
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/items.php
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'DB.php';
|
||||||
|
|
||||||
|
$db = new DB('shop.db');
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
$_SESSION['user'] = $db->retrieve_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $db->get_items();
|
||||||
|
$cart_count = $db->get_cart_count();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="uk">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Сторінка товарів</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Продовольчий магазин "Весна"</h1>
|
||||||
|
<h3> Добрий день <?php echo $_SESSION['user']['name'] ?> </h3>
|
||||||
|
<nav>
|
||||||
|
<a href="index.php">Головна</a>
|
||||||
|
|
|
||||||
|
<a href="items.php">Товари</a>
|
||||||
|
|
|
||||||
|
<a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?>)</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h2>Доступні товари</h2>
|
||||||
|
<div class="product-list">
|
||||||
|
<?php foreach ($items as $item): ?>
|
||||||
|
<div>
|
||||||
|
<h2><?php echo htmlspecialchars($item['name']); ?></h2>
|
||||||
|
<h3>Ціна: <?php echo number_format($item['price'], 2); ?> грн</h3>
|
||||||
|
|
||||||
|
<form action="handle_cart.php" method="POST">
|
||||||
|
<input type="hidden" name="product_id" value="<?php echo htmlspecialchars($item['id']); ?>">
|
||||||
|
<label for="quantity_<?php echo htmlspecialchars($item['id']); ?>">Кількість:</label>
|
||||||
|
<input type="number" id="quantity_<?php echo htmlspecialchars($item['id']); ?>" name="quantity" value="0" min="0" max="100">
|
||||||
|
<button type="submit">Купити</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<nav>
|
||||||
|
<a href="index.php">Головна</a>
|
||||||
|
|
|
||||||
|
<a href="items.php">Товари</a>
|
||||||
|
|
|
||||||
|
<a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?>)</a>
|
||||||
|
</nav>
|
||||||
|
<p>© <?php echo date("Y"); ?> ТОВ "Весна". Усі права захищені.</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
== Кошик покупок (cart.php)
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/cart.php
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'DB.php';
|
||||||
|
|
||||||
|
$db = new DB("shop.db");
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
$_SESSION['user'] = $db->retrieve_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart_items = $db->get_cart();
|
||||||
|
$cart_total = $db->get_cart_total();
|
||||||
|
$cart_count = $db->get_cart_count();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="uk">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Кошик</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Продовольчий магазин "Весна"</h1>
|
||||||
|
<h3> Добрий день <?php echo $_SESSION['user']['name'] ?> </h3>
|
||||||
|
<nav>
|
||||||
|
<a href="index.php">Головна</a>
|
||||||
|
|
|
||||||
|
<a href="items.php">Товари</a>
|
||||||
|
|
|
||||||
|
<a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?>)</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<?php if (empty($cart_items)): ?>
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-evenly;">
|
||||||
|
<h3>Ваш кошик порожній <a href="items.php">Перейти до покупок</a> </h3>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-evenly;">
|
||||||
|
<h3>Ваш кошик</h3>
|
||||||
|
<h3 class="cart-summary">
|
||||||
|
Загальна сума: <?php echo number_format($cart_total, 2); ?> грн
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<button type="submit">Сплатити</button>
|
||||||
|
|
||||||
|
<button onclick="fetch('handle_cart.php', {'method': 'DELETE'}).then(_ => { location.reload(); });">Очистити</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="product-list">
|
||||||
|
<?php foreach ($cart_items as $item): ?>
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
<?php echo htmlspecialchars($item['name']); ?>
|
||||||
|
<br>
|
||||||
|
<?php echo htmlspecialchars($item['count']); ?> шт.
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<span>Ціна за одиницю: <?php echo number_format($item['price'], 2); ?> грн</span>
|
||||||
|
<br>
|
||||||
|
<span>Загальна ціна: <?php echo number_format($item['total_price'], 2); ?> грн</span>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<button
|
||||||
|
style="width: 100%;"
|
||||||
|
onclick="
|
||||||
|
fetch(
|
||||||
|
'handle_cart.php?product_id=<?php echo htmlspecialchars($item['id']); ?>',
|
||||||
|
{ 'method': 'DELETE' }
|
||||||
|
).then(_ => { location.reload(); });">
|
||||||
|
Видалити
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<nav>
|
||||||
|
<a href="index.php">Головна</a>
|
||||||
|
|
|
||||||
|
<a href="items.php">Товари</a>
|
||||||
|
|
|
||||||
|
<a href="cart.php">Кошик (<?php echo $cart_count ?? 0; ?>)</a>
|
||||||
|
</nav>
|
||||||
|
<p>© <?php echo date("Y"); ?> ТОВ "Весна". Усі права захищені.</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
== Обробник кошика (handle_cart.php)
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/handle_cart.php
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'DB.php';
|
||||||
|
|
||||||
|
$db_path = 'shop.db';
|
||||||
|
$db = new DB($db_path);
|
||||||
|
|
||||||
|
switch ($_SERVER['REQUEST_METHOD']) {
|
||||||
|
case 'POST':
|
||||||
|
$product_id = filter_input(INPUT_POST, 'product_id', FILTER_VALIDATE_INT);
|
||||||
|
$quantity = filter_input(INPUT_POST, 'quantity', FILTER_VALIDATE_INT);
|
||||||
|
|
||||||
|
if ($product_id !== false && $product_id !== null && $quantity !== false && $quantity !== null) {
|
||||||
|
try {
|
||||||
|
$db->add_to_cart($product_id, $quantity);
|
||||||
|
} catch (DbException $e) {
|
||||||
|
error_log("Cart handling error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header('Location: items.php');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'DELETE':
|
||||||
|
$product_id = filter_input(INPUT_GET, 'product_id', FILTER_VALIDATE_INT);
|
||||||
|
|
||||||
|
if ($product_id === null) {
|
||||||
|
try {
|
||||||
|
$db->empty_cart();
|
||||||
|
} catch (DbException $e) {
|
||||||
|
error_log("Cart handling error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
} else if ($product_id !== false) {
|
||||||
|
try {
|
||||||
|
$db->remove_from_cart($product_id);
|
||||||
|
} catch (DbException $e) {
|
||||||
|
error_log("Cart handling error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: cart.php');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit();
|
||||||
|
```
|
||||||
|
|
||||||
|
== Клас для роботи з базою даних (DB.php)
|
||||||
|
GitHub репозиторій: https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab3/smp-pzpi-23-2-sytnyk-yehor-lab3/DB.php
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class DbException extends Exception {}
|
||||||
|
|
||||||
|
class DB
|
||||||
|
{
|
||||||
|
private $pdo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes database
|
||||||
|
*
|
||||||
|
* @param string $db_path
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function __construct($db_path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->pdo = new PDO("sqlite:" . $db_path);
|
||||||
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Connection to DB failed.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ініціалізація таблиць та початкових даних
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
name TEXT,
|
||||||
|
age TEXT
|
||||||
|
);
|
||||||
|
");
|
||||||
|
if ($this->pdo->query("SELECT COUNT(*) FROM settings;")->fetchColumn() == 0) {
|
||||||
|
$this->pdo->exec("INSERT INTO settings (name, age) VALUES ('user', 0);");
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising settings table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS items (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
price REAL NOT NULL
|
||||||
|
);
|
||||||
|
");
|
||||||
|
if ($this->pdo->query("SELECT COUNT(id) FROM items;")->fetchColumn() == 0) {
|
||||||
|
$this->pdo->exec("
|
||||||
|
INSERT INTO items (name, price) VALUES ('Молоко пастеризоване', 32.50);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Хліб чорний', 18.00);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сир білий', 85.00);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сметана 20%', 45.80);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Кефір 1%', 28.50);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Вода газована', 25.00);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Печиво \"Весна\"', 42.30);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Масло вершкове', 125.00);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Йогурт натуральний', 38.90);
|
||||||
|
INSERT INTO items (name, price) VALUES ('Сік апельсиновий', 55.00);
|
||||||
|
");
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS cart (
|
||||||
|
id INTEGER NOT NULL UNIQUE,
|
||||||
|
count INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(id) REFERENCES items(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
");
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error initialising cart table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve user information from the database
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function retrieve_user(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query("SELECT name, age FROM settings;");
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error retrieving user info.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items from the database.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_items(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query("SELECT id, name, price FROM items ORDER BY id;");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error retrieving data from the items table.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all items in the cart from the database.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT
|
||||||
|
cart.id,
|
||||||
|
items.name,
|
||||||
|
items.price,
|
||||||
|
cart.count,
|
||||||
|
ROUND(items.price * cart.count, 2) as total_price
|
||||||
|
FROM cart
|
||||||
|
INNER JOIN items ON cart.id = items.id
|
||||||
|
ORDER BY cart.id;"
|
||||||
|
);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error retrieving cart items.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total items count in cart
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart_count(): int
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query("SELECT COALESCE(SUM(count), 0) FROM cart;");
|
||||||
|
return (int)$stmt->fetchColumn();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error getting cart count.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total price of all items in cart
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function get_cart_total(): float
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->query(
|
||||||
|
"SELECT COALESCE(SUM(items.price * cart.count), 0.0)
|
||||||
|
FROM cart
|
||||||
|
INNER JOIN items ON cart.id = items.id;"
|
||||||
|
);
|
||||||
|
return (float)$stmt->fetchColumn();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error calculating cart total.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add item to the cart or update its quantity.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @param int $count
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error or item doesn't exist.
|
||||||
|
*/
|
||||||
|
public function add_to_cart($id, $count): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Check if the item exists
|
||||||
|
$item = $this->get_item_by_id($id);
|
||||||
|
if (!$item) {
|
||||||
|
throw new DbException("Item with ID $id does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If count is 0 or less, remove the item from the cart
|
||||||
|
if ($count <= 0) {
|
||||||
|
return $this->remove_from_cart($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert or update the cart item
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
"INSERT INTO cart (id, count)
|
||||||
|
VALUES (:id, :count)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
count = excluded.count;"
|
||||||
|
);
|
||||||
|
return $stmt->execute(['id' => $id, 'count' => $count]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error adding/updating item in the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty the cart
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function empty_cart(): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("DELETE FROM cart");
|
||||||
|
return $stmt->execute();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error removing item from the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cart
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws DbException If there's a database error.
|
||||||
|
*/
|
||||||
|
public function remove_from_cart($id): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$stmt = $this->pdo->prepare("DELETE FROM cart WHERE id = :id");
|
||||||
|
return $stmt->execute(['id' => $id]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new DbException("Error removing item from the cart.\nCaused by: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
> [!NOTE]
|
||||||
|
> Викладач: Сокорчук І. П.
|
||||||
|
>
|
||||||
|
> Оцінка: In Progress
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
require_once 'src/Database/DB.php';
|
||||||
|
require_once 'src/Database/CartRepository.php';
|
||||||
|
require_once 'src/Controllers/HomeController.php';
|
||||||
|
require_once 'src/Controllers/ItemsController.php';
|
||||||
|
require_once 'src/Controllers/CartController.php';
|
||||||
|
require_once 'src/Controllers/AuthController.php';
|
||||||
|
require_once 'src/Controllers/ProfileController.php';
|
||||||
|
|
||||||
|
$db = new DB("shop.db");
|
||||||
|
|
||||||
|
$request = $_GET['page'] ?? 'home';
|
||||||
|
$action = $_GET['action'] ?? 'index';
|
||||||
|
|
||||||
|
$protected_pages = ['home', 'items', 'cart', 'profile'];
|
||||||
|
$public_pages = ['login', 'register'];
|
||||||
|
|
||||||
|
if (in_array($request, $protected_pages) && !isset($_SESSION['user'])) {
|
||||||
|
header('Location: ?page=404');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch ($request) {
|
||||||
|
case 'home':
|
||||||
|
$controller = new HomeController($db);
|
||||||
|
$controller->index();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'items':
|
||||||
|
$controller = new ItemsController($db);
|
||||||
|
$controller->index();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'cart':
|
||||||
|
$controller = new CartController($db);
|
||||||
|
if ($action === 'add' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$controller->add();
|
||||||
|
} elseif ($action === 'remove' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$controller->remove();
|
||||||
|
} elseif ($action === 'clear' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$controller->clear();
|
||||||
|
} else {
|
||||||
|
$controller->index();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'login':
|
||||||
|
$controller = new AuthController($db);
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$controller->login();
|
||||||
|
} else {
|
||||||
|
$controller->showLogin();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'register':
|
||||||
|
$controller = new AuthController($db);
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$controller->register();
|
||||||
|
} else {
|
||||||
|
$controller->showRegister();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'logout':
|
||||||
|
$controller = new AuthController($db);
|
||||||
|
$controller->logout();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'profile':
|
||||||
|
$controller = new ProfileController($db);
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$controller->updateProfile();
|
||||||
|
} else {
|
||||||
|
$controller->showProfile();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
http_response_code(404);
|
||||||
|
include 'templates/pages/404.php';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("Application error: " . $e->getMessage());
|
||||||
|
http_response_code(500);
|
||||||
|
include 'templates/pages/error.php';
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
@@ -0,0 +1,43 @@
|
|||||||
|
body {
|
||||||
|
font-family: monospace;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #333;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
header,
|
||||||
|
footer {
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:not(:hover) {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 20px auto;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-list>* {
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
class AuthController
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
public function __construct(DB $db)
|
||||||
|
{
|
||||||
|
$this->db = $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showLogin(): void
|
||||||
|
{
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
header('Location: ?page=home');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = ['title' => 'Вхід в систему'];
|
||||||
|
$this->render('login', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(): void
|
||||||
|
{
|
||||||
|
$username = trim($_POST['username'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if (empty($username) || empty($password)) {
|
||||||
|
$error = 'Заповніть всі поля';
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$user = $this->db->authenticate_user($username, $password);
|
||||||
|
if ($user) {
|
||||||
|
$_SESSION['user'] = $user;
|
||||||
|
header('Location: ?page=home');
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$error = 'Невірні дані для входу';
|
||||||
|
}
|
||||||
|
} catch (DbException $e) {
|
||||||
|
$error = 'Помилка системи';
|
||||||
|
error_log("Login error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = ['title' => 'Вхід в систему', 'error' => $error];
|
||||||
|
$this->render('login', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showRegister(): void
|
||||||
|
{
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
header('Location: ?page=home');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = ['title' => 'Реєстрація'];
|
||||||
|
$this->render('register', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
$username = trim($_POST['username'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$surname = trim($_POST['surname'] ?? '');
|
||||||
|
$age = (int)($_POST['age'] ?? 0);
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if (empty($username) || empty($password) || empty($name)) {
|
||||||
|
$error = 'Заповніть всі обов\'язкові поля';
|
||||||
|
} elseif (strlen($password) < 6) {
|
||||||
|
$error = 'Пароль повинен містити мінімум 6 символів';
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
if ($this->db->register_user($username, $password, $name, $surname, $age)) {
|
||||||
|
$user = $this->db->authenticate_user($username, $password);
|
||||||
|
$_SESSION['user'] = $user;
|
||||||
|
header('Location: ?page=home');
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$error = 'Користувач з таким іменем вже існує';
|
||||||
|
}
|
||||||
|
} catch (DbException $e) {
|
||||||
|
$error = 'Помилка реєстрації';
|
||||||
|
error_log("Registration error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = ['title' => 'Реєстрація', 'error' => $error];
|
||||||
|
$this->render('register', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout(): void
|
||||||
|
{
|
||||||
|
session_destroy();
|
||||||
|
header('Location: ?page=login');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int,mixed> $data
|
||||||
|
*/
|
||||||
|
private function render(string $template, array $data = []): void
|
||||||
|
{
|
||||||
|
extract($data);
|
||||||
|
include 'templates/pages/' . $template . '.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user