import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.FileChooser;
import javafx.util.StringConverter;
// Pane يمثل حاوية النافذة التي سنظهرها عند تشغيل التطبيق لهذا جعلناه يرث من الكلاس AllProductsPane الكلاس
// ليحصل على المعلومات الضرورية التي نحتاجها للإتصال بخادم قاعدة البيانات DBInfo و جعلناه ينفذ الإنترفيس
public class AllProductsPane extends Pane implements DBInfo {
// AllProductsPane هنا قمنا بإنشاء جميع الأشياء التي سنضيفها في الحاوية التي يمثلها الكائن الذي ننشئه من الكلاس
TableView table = new TableView();
TableColumn<Product, Integer> columnId = new TableColumn("ID");
TableColumn<Product, String> columnName = new TableColumn("Name");
TableColumn<Product, Double> columnPrice = new TableColumn("Price ($)");
TableColumn<Product, String> columnAddedDate = new TableColumn("Added Dated");
Label productImage = new Label("No image found");
Label idLabel = new Label("ID");
Label nameLabel = new Label("Name");
Label priceLabel = new Label("Price");
Label dateLabel = new Label("Date");
Label moveFastLabel = new Label("Move Fast");
Label searchLabel = new Label("Search");
TextField idField = new TextField();
TextField nameField = new TextField();
TextField priceField = new TextField();
TextField searchField = new TextField();
Button updateImageButton = new Button("Update Image");
Button insertButton = new Button("Add New", new ImageView(new Image(getClass().getResourceAsStream("/images/insert.png"))));
Button updateButton = new Button("Update", new ImageView(new Image(getClass().getResourceAsStream("/images/update.png"))));
Button deleteButton = new Button("Delete", new ImageView(new Image(getClass().getResourceAsStream("/images/delete.png"))));
Button selectFirstButton = new Button("First", new ImageView(new Image(getClass().getResourceAsStream("/images/first.png"))));
Button selectLastButton = new Button("Last", new ImageView(new Image(getClass().getResourceAsStream("/images/last.png"))));
Button selectNextButton = new Button("Next", new ImageView(new Image(getClass().getResourceAsStream("/images/next.png"))));
Button selectPreviousButton = new Button("Previous", new ImageView(new Image(getClass().getResourceAsStream("/images/previous.png"))));
Button exitButton = new Button("Exit", new ImageView(new Image(getClass().getResourceAsStream("/images/exit.png"))));
DatePicker datePicker = new DatePicker();
// التي سنعرضها في الجدول ( Product سنخزن فيه معلومات جميع المنتجات ( التي هي عبارة عن كائنات من الكلاس data الكائن
ObservableList<Product> data = FXCollections.observableArrayList();
// و الذي يمثل النافذة المنبثقة التي تسمح لنا بإضافة منتج جديد AddNewProductDialog هنا قمنا بإنشاء كائن من الكلاس
AddNewProductDialog addProductDialog = new AddNewProductDialog(data);
// الكائن التالي سنخزن فيه الصورة التي تظهر للمنتج الذي يتم النقر عليه في الجدول أو التي يختارها المستخدم بشكل مؤقت عند تحديث صورة المنتج
File selectedFile = null;
// ليظهر كالتالي: يوم - شهر - سنة datePicker سنستخدمه لتحديد الطريقة التي سيظهر بها التاريخ بداخل الكائن dateFormat المتغير
final String dateFormat = "yyyy-MM-dd";
// لأننا سنستخدمه لعرض أي خطأ أو معلومة أمام المستخدم بداخل نافذة منبثقة صغيرة بشكل مختصر SpecialAlert هنا قمنا بإنشاء كائن من الكلاس
SpecialAlert alert = new SpecialAlert();
// AllProductsPane هذا كونستركتور الكلاس
public AllProductsPane() {
// dateFormat يظهر بالفورمات الذي يشير إليه المتغير datePicker لها لجعل التاريخ الذي يظهر في الكائن dateFormatter() و تمرير الدالة setConverter() هنا قمنا باستدعاء الدالة
datePicker.setConverter(dateFormatter());
// table هنا وضعنا الأعمدة الأربعة في الكائن
table.getColumns().addAll(columnId, columnName, columnPrice, columnAddedDate);
// table سيتم عرضها في الكائن data هنا قمنا بتحديد أن البيانات التي ستكون موجودة في الكائن
// تم إضافته في الجدول Product الموجودة في كل كائن id سيُعرض فيه قيم الـ columnId هنا قلنا أن العامود
columnId.setCellValueFactory(new PropertyValueFactory<>("id"));
// تم إضافته في الجدول Product الموجودة في كل كائن name سيُعرض فيه قيم الـ columnName هنا قلنا أن العامود
columnName.setCellValueFactory(new PropertyValueFactory<>("name"));
// تم إضافته في الجدول Product الموجودة في كل كائن price سيُعرض فيه قيم الـ columnPrice هنا قلنا أن العامود
columnPrice.setCellValueFactory(new PropertyValueFactory<>("price"));
// تم إضافته في الجدول Product الموجودة في كل كائن addedDate سيُعرض فيه قيم الـ columnAddedDate هنا قلنا أن العامود
columnAddedDate.setCellValueFactory(new PropertyValueFactory<>("addedDate"));
// هنا قمنا بجعل حجم أعمدة الجدول ينقسم بالتساوي على عددهم مع إبقاء المستخدم قادر على تعديل أحجامهم بواسطة الفأرة
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
// AllProductsPane هنا قمنا بتحديد حجم كل شيء سيتم إضافته في الحاوية التي يمثلها الكائن الذي ننشئه من الكلاس
productImage.setPrefSize(270, 244);
updateImageButton.setPrefSize(130, 35);
idLabel.setPrefSize(50, 40);
idField.setPrefSize(270, 40);
nameLabel.setPrefSize(50, 40);
nameField.setPrefSize(270, 40);
priceLabel.setPrefSize(50, 40);
priceField.setPrefSize(270, 40);
dateLabel.setPrefSize(50, 40);
datePicker.setPrefSize(270, 40);
deleteButton.setPrefSize(130, 40);
updateButton.setPrefSize(130, 40);
table.setPrefSize(520, 505);
searchField.setPrefSize(255, 36);
searchLabel.setPrefSize(115, 40);
insertButton.setPrefSize(130, 60);
moveFastLabel.setPrefSize(190, 30);
selectFirstButton.setPrefSize(130, 40);
selectLastButton.setPrefSize(130, 40);
selectNextButton.setPrefSize(130, 40);
selectPreviousButton.setPrefSize(130, 40);
exitButton.setPrefSize(130, 40);
// AllProductsPane هنا قمنا بتحديد مكان ظهور كل شيء سيتم إضافته في الحاوية التي يمثلها الكائن الذي ننشئه من الكلاس
productImage.setTranslateX(80);
productImage.setTranslateY(40);
updateImageButton.setTranslateX(150);
updateImageButton.setTranslateY(295);
idLabel.setTranslateX(20);
idLabel.setTranslateY(355);
idField.setTranslateX(80);
idField.setTranslateY(355);
nameLabel.setTranslateX(20);
nameLabel.setTranslateY(405);
nameField.setTranslateX(80);
nameField.setTranslateY(405);
priceLabel.setTranslateX(20);
priceLabel.setTranslateY(455);
priceField.setTranslateX(80);
priceField.setTranslateY(455);
dateLabel.setTranslateX(20);
dateLabel.setTranslateY(505);
datePicker.setTranslateX(80);
datePicker.setTranslateY(505);
deleteButton.setTranslateX(80);
deleteButton.setTranslateY(575);
updateButton.setTranslateX(220);
updateButton.setTranslateY(575);
table.setTranslateX(377);
searchField.setTranslateX(530);
searchField.setTranslateY(577);
searchLabel.setTranslateX(460);
searchLabel.setTranslateY(575);
insertButton.setTranslateX(920);
insertButton.setTranslateY(40);
moveFastLabel.setTranslateX(890);
moveFastLabel.setTranslateY(150);
selectFirstButton.setTranslateX(920);
selectFirstButton.setTranslateY(200);
selectLastButton.setTranslateX(920);
selectLastButton.setTranslateY(250);
selectNextButton.setTranslateX(920);
selectNextButton.setTranslateY(300);
selectPreviousButton.setTranslateX(920);
selectPreviousButton.setTranslateY(350);
exitButton.setTranslateX(920);
exitButton.setTranslateY(575);
// AllProductsPane هنا قمنا بتحديد خصائص الأشياء التي سيتم إضافتها في الحاوية التي يمثلها الكائن الذي ننشئه من الكلاس
updateImageButton.setFont(Font.font("Arial", FontWeight.BOLD, 13));
idLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
idField.setFont(Font.font("Arial", FontWeight.BOLD, 15));
nameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
nameField.setFont(Font.font("Arial", FontWeight.BOLD, 15));
priceLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
priceField.setFont(Font.font("Arial", FontWeight.BOLD, 15));
dateLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
datePicker.getEditor().setFont(Font.font("Arial", FontWeight.BOLD, 15));
deleteButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
updateButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
insertButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
searchField.setFont(Font.font("Arial", FontWeight.BOLD, 15));
searchLabel.setFont(Font.font("Arial", FontWeight.BOLD, 17));
moveFastLabel.setFont(Font.font("Arial", FontWeight.BOLD, 22));
selectFirstButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
selectLastButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
selectNextButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
selectPreviousButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
exitButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
productImage.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
idField.setStyle("-fx-border-color: lightgray; -fx-border-width: 2; -fx-background-color: #eee;");
nameField.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
priceField.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
searchField.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
datePicker.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
productImage.setAlignment(Pos.CENTER);
moveFastLabel.setAlignment(Pos.CENTER);
idField.setEditable(false);
// AllProductsPane هنا قمنا بإضافة جميع الأشياء التي قمنا بإنشائها و تعديل تصيممها في الكائن الذي ننشئه من الكلاس
this.getChildren().add(table);
this.getChildren().add(productImage);
this.getChildren().add(updateImageButton);
this.getChildren().add(idLabel);
this.getChildren().add(idField);
this.getChildren().add(nameLabel);
this.getChildren().add(nameField);
this.getChildren().add(priceLabel);
this.getChildren().add(priceField);
this.getChildren().add(dateLabel);
this.getChildren().add(datePicker);
this.getChildren().add(insertButton);
this.getChildren().add(updateButton);
this.getChildren().add(deleteButton);
this.getChildren().add(searchField);
this.getChildren().add(searchLabel);
this.getChildren().add(moveFastLabel);
this.getChildren().add(selectFirstButton);
this.getChildren().add(selectLastButton);
this.getChildren().add(selectNextButton);
this.getChildren().add(selectPreviousButton);
this.getChildren().add(exitButton);
// لملئ الجدول بالمنتجات fillTheTable() هنا قمنا باستدعاء الدالة
// لإظهار معلومات و صورة أول منتج في الجدول في الحقول الموضوعة بجانبه showFirstProduct() هنا قمنا باستدعاء الدالة
// لعرض معلومات المنتج بجانب الجدول showProduct() سيتم استدعاء الدالة table هنا قمنا بتحديد أنه عند النقر على أي سطر في الجدول الذي يمثله الكائن
table.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if (newSelection != null) {
showProduct(table.getSelectionModel().getSelectedIndex());
// حتى يتم إظهار النافذة المنبثقة addProductDialog من الكائن display() سيتم استدعاء الدالة insertButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
insertButton.setOnAction(Action -> {
addProductDialog.display(true);
// لعرض معلومات أول منتج في الجدول بجانبه showFirstProduct() سيتم استدعاء الدالة selectFirstButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
selectFirstButton.setOnAction(Action -> {
// لعرض معلومات آخر منتج في الجدول بجانبه showLastProduct() سيتم استدعاء الدالة selectLastButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
selectLastButton.setOnAction(Action -> {
// لعرض معلومات المنتج التالي في الجدول بجانبه showNextProduct() سيتم استدعاء الدالة selectNextButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
selectNextButton.setOnAction(Action -> {
// لعرض معلومات المنتج السابق في الجدول بجانبه showPreviousProduct() سيتم استدعاء الدالة selectPreviousButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
selectPreviousButton.setOnAction(Action -> {
// لفتح نافذة منبثقة تسمح بتغير صورة المنتج chooseImage() سيتم استدعاء الدالة updateImageButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
updateImageButton.setOnAction(Action -> {
// لتحديث بيانات المنتج في قاعدة البيانات و في الجدول updateProduct() سيتم استدعاء الدالة updateButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
updateButton.setOnAction(Action -> {
// لحذف المنتج و صورته بشكل نهائي deleteProduct() سيتم استدعاء الدالة deleteButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
deleteButton.setOnAction(Action -> {
// لإغلاق البرنامج exit() سيتم استدعاء الدالة exitButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
exitButton.setOnAction(Action -> {
// للبحث في الجدول بناءاً على النص الذي يتم إدخاله search() سيتم استدعاء الدالة searchField هنا قمنا بتحديد أنه عند كتابة أو مسح أي حرف في مربع النص الذي يمثله الكائن
searchField.textProperty().addListener((obs, oldText, newText) -> {
// searchField لتحديد كيف سيتم البحث في الجدول عند إدخال أي كلمة في مربع البحث الذي يمثله الكائن search() هنا قمنا ببناء الدالة
// keyword سيتم تخزينه بشكل مؤقت في المتغير searchField النص الذي يتم إدخاله في الكائن
String keyword = searchField.getText();
// في الجدول data سيتم عرض جميع البيانات الموجودة في الكائن searchField إذا كان لا يوجد أي نص مدخل في الكائن
if (keyword.equals("")) {
// data سيتم إنشاء نسخة من البيانات ( أي المنتجات ) الموجودة في الكائن searchField إذا كان يوجد أي نص مدخل في الكائن
// keyword فيها ) على نفس الأحرف الموجودة في المتغير name التي يحتوي إسمها ( أو الخاصية
ObservableList<Product> filteredData = FXCollections.observableArrayList();
for (Product product : data) {
if(product.getName().contains(keyword))
filteredData.add(product);
// في الأخير سيتم عرض نسخة البيانات المفلترة التي تم إنشاؤها في الجدول بدلاً من البيانات الأصلية التي كانت معروضة فيه
table.setItems(filteredData);
// و التي سنقوم باستدعائها كلما تم تحديد منتج جديد في الجدول لعرض جميع المعلومات showProduct() هنا قمنا ببناء الدالة
// الخاص فيه id المتوفرة عنه بجانب الجدول, مع الإشارة إلى أنه سيتم جلب جميع المعلومات الخاصة بالمنتج بناءاً على رقم الـ
private void showProduct(int index)
idField.setText(data.get(index).getId() + "");
nameField.setText(data.get(index).getName());
priceField.setText(data.get(index).getPrice() + "");
datePicker.getEditor().setText(data.get(index).getAddedDate());
// في حال كان المنتج لا يملك صورة "No image found" الشرطين التاليين فكرتهما أنه سيتم وضع النص
if (data.get(index).getImageUrl() == null) {
productImage.setGraphic(null);
productImage.setText("No image found");
productImage.setText("");
productImage.setGraphic(new ImageView(new Image(
new File(data.get(index).getImageUrl()).toURI().toString(),
// لتحديد أول منتج في الجدول ثم عرض معلوماته showFirstProduct() هنا قمنا ببناء الدالة
private void showFirstProduct()
table.getSelectionModel().select(0);
// لتحديد آخر منتج في الجدول ثم عرض معلوماته showlastProduct() هنا قمنا ببناء الدالة
private void showLastProduct()
table.getSelectionModel().select(data.size() - 1);
showProduct(data.size() - 1);
// لتحديد المنتج التالي في الجدول ثم عرض معلوماته showNextProduct() هنا قمنا ببناء الدالة
private void showNextProduct()
if (table.getSelectionModel().getSelectedIndex() < data.size() - 1) {
int currentSelectedRow = table.getSelectionModel().getSelectedIndex() + 1;
table.getSelectionModel().select(currentSelectedRow);
showProduct(currentSelectedRow);
// لتحديد المنتج السابق في الجدول ثم عرض معلوماته showPreviousProduct() هنا قمنا ببناء الدالة
private void showPreviousProduct()
if (table.getSelectionModel().getSelectedIndex() > 0) {
int currentSelectedRow = table.getSelectionModel().getSelectedIndex() - 1;
table.getSelectionModel().select(currentSelectedRow);
showProduct(currentSelectedRow);
// لجعل المستخدم قادر على إختيار صورة موجودة في حاسوبه updateImage() قمنا ببناء الدالة
// updateImageButton سيتم إستدعاء هذه الدالة عندما يقوم المستخدم بالنقر على الزر الذي يمثله الكائن
private void updateImage()
// الشرط التالي معناه أنه إذا لم يكن هناك أي سطر محدد في الجدول أثناء النقر على الزر سيتم إظهار رسالة
// تنبيه تبلغ المستخدم أنه يجب أن يحدد السطر ( أي المنتج ) الذي يريد تحديث صورته قبل أن ينقر على الزر
if(table.getSelectionModel().getSelectedItem() == null)
alert.show("Message", "Select the item that you want to update its image first", AlertType.INFORMATION);
// و الذي سيمثل نافذة منبثقة لإختيار صورة من الجهاز FileChooser هنا قمنا بإنشاء كائن من الكلاس
FileChooser fileChooser = new FileChooser();
// هنا قمنا بتحديد نوع الصور التي يمكنك للمستخدم إختيارها من جهازه
fileChooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter("Select a .JPG .PNG .GIF image", "*.jpg", "*.png", "*.gif")
// لتخزين الملف الذي قد يختاره المستخدم من الحاسوب selectedFile و قمنا بتجهيز الكائن showOpenDialog() بواسطة الدالة fileChooser هنا قمنا بإظهار الكائن
selectedFile = fileChooser.showOpenDialog(null);
// Open فهذا يعني أن المستخدم قام باختيار صورة موجودة على جهازه و نقر على الزر null لا تساوي selectedFile في حال كانت قيمة الكائن
if (selectedFile != null) {
// createImagePath لإنشاء نسخة من الصورة التي اختارها مع حفظ المسار الذي أنشئت فيه في المتغير saveSelectedImage() هنا قمنا باستدعاء الدالة
String createImagePath = Common.saveSelectedImage(selectedFile);
// لتخزين نص الإستعلام الذي سيتم تنفيذه query هنا قمنا بالإتصال بخادم قاعدة البيانات و تجهزي المتغير
Connection con = Common.getConnection();
// الخاص بالمنتج id هنا قمنا بتجهيز نص الإستعلام الذي يقضي بتحديث مسار الصورة بناءاً على رقم الـ
String query = "UPDATE products SET image_url = ? WHERE id = ?";
// و الذي سيسمح لنا بتنفيذ الإستعلام PreparedStatement هنا قمنا بتجهيز كائن نوعه
PreparedStatement ps = con.prepareStatement(query);
// الخاص بالمنتج مكان ثاني علامة إستفهام id هنا مررنا مسار الصورة مكان أول علامة إستفهام و رقم الـ
ps.setString(1, createImagePath);
ps.setInt(2, Integer.parseInt(idField.getText()));
// هنا قمنا بتنفيذ الإستعلام
// هنا قمنا بإغلاق الإتصال مع خادم قاعدة البيانات
// selectedProduct هنا قمنا بتخزين بيانات المنتج الحالي الذي تم تغيير معلوماته في الكائن
Product selectedProduct = (Product) table.getSelectionModel().getSelectedItem();
// هنا قمنا بمسح صورة المنتج القديمة من على جهاز المستخدم
Common.deleteImage(selectedProduct.getImageUrl());
// data و الذي سيتحدث بشكل تلقائي في الكائن selectedProduct هنا قمنا بتحديث رابط صورة المنتج الجديدة في الكائن
selectedProduct.setImageUrl(createImagePath);
// هنا قمنا بعرض صورة المنتج الجديدة
productImage.setText("");
productImage.setGraphic(new ImageView(new Image(
selectedFile.toURI().toString(), 224, 224, true, true)));
alert.show("Error", "Failed to update Image", AlertType.ERROR);
// لتحديد كيف سيتم تحديث بيانات المنتج في قاعدة البيانات updateProduct() قمنا ببناء الدالة
// updateButton سيتم إستدعاء هذه الدالة عندما يقوم المستخدم بالنقر على الزر الذي يمثله الكائن
private void updateProduct()
// الشرط التالي معناه أنه إذا لم يكن هناك أي سطر محدد في الجدول أثناء النقر على الزر سيتم إظهار رسالة
// تنبيه تبلغ المستخدم أنه يجب أن يحدد السطر ( أي المنتج ) الذي يريد تحديث بياناته قبل أن ينقر على الزر
if(table.getSelectionModel().getSelectedItem() == null)
alert.show("Message", "Select the item that you want to update first", AlertType.INFORMATION);
// الشرط التالي معناه أنه إذا تم التشييك على الحقول و كان لا يوجد أي خطأ أو نقص في المعلومات المطلوب إدخالها
// سيتم تنفيذ باقي الأوامر الموجودة في الدالة تحديث معلومات المنتج في قاعدة البيانات و في الجدول أيضاً
if (checkInputs() == false) {
// selectedProduct هنا قمنا بتخزين بيانات المنتج الحالي الذي تم تغيير معلوماته في الكائن
Product selectedProduct = (Product) table.getSelectionModel().getSelectedItem();
// لتخزين نص الإستعلام الذي سيتم تنفيذه query هنا قمنا بالإتصال بخادم قاعدة البيانات و تجهزي المتغير
Connection con = getConnection();
// إذا كان المنتج لا يملك صورة, سيتم تحديث كل حقوله في قاعدة البيانات مع عدا حقل الصورة
query = "UPDATE products SET name = ?, price = ?, added_date = ? WHERE id = ?";
PreparedStatement ps = con.prepareStatement(query);
ps.setString(1, nameField.getText());
ps.setDouble(2, Double.valueOf(priceField.getText()));
ps.setDate(3, Date.valueOf(datePicker.getEditor().getText()));
ps.setInt(4, Integer.parseInt(idField.getText()));
// هنا قمنا بإغلاق الإتصال مع خادم قاعدة البيانات
// هنا قمنا بوضوع جميع المعلومات الجديدة في الكائن الذي تم تحديده في الأساس في الجدول
selectedProduct.setName(nameField.getText());
selectedProduct.setPrice(Double.valueOf(priceField.getText()));
selectedProduct.setAddedDate(datePicker.getEditor().getText());
// من أجل تحديث بيانات الجدول, أي لإظهار البيانات التي تم تحديثها فيه refresh() هنا قمنا باستدعاء الدالة
// هنا قمنا بإظهار نافذة منبثقة تخبرنا أنه تم تحديث البيانات بنجاح
alert.show("Product Successfully updated",
"Product information has been successfully updated",
// في حال حدوث أي خطأ, سيتم عرضه بداخل نافذة منبثقة
alert.show("Error", e.getMessage(), AlertType.ERROR);
// لتحديد كيف سيتم تحديث بيانات المنتج في قاعدة البيانات updateProduct() قمنا ببناء الدالة
// updateButton سيتم إستدعاء هذه الدالة عندما يقوم المستخدم بالنقر على الزر الذي يمثله الكائن
private void deleteProduct()
// الشرط التالي معناه أنه إذا لم يكن هناك أي سطر محدد في الجدول أثناء النقر على الزر سيتم إظهار رسالة
// تنبيه تبلغ المستخدم أنه يجب أن يحدد السطر ( أي المنتج ) الذي يريد حذفه قبل أن ينقر على الزر
if(table.getSelectionModel().getSelectedItem() == null)
alert.show("Message", "Select the item that you want to delete first", AlertType.INFORMATION);
// selectedProduct هنا قمنا بتخزين بيانات المنتج الحالي الذي تم تغيير معلوماته في الكائن
Product selectedProduct = (Product) table.getSelectionModel().getSelectedItem();
// لتخزين نص الإستعلام الذي سيتم تنفيذه query هنا قمنا بالإتصال بخادم قاعدة البيانات و تجهزي المتغير
Connection con = getConnection();
// الخاص فيه id هنا قمنا بتجهيز نص الإستعلام الذي يقضي بمسح المنتج بناءاً على رقم الـ
String query = "DELETE FROM products WHERE id = ?";
// و الذي سيسمح لنا بتنفيذ الإستعلام PreparedStatement هنا قمنا بتجهيز كائن نوعه
PreparedStatement ps = con.prepareStatement(query);
// الخاص بالمنتج مكان علامة الإستفهام id هنا مررنا رقم الـ
ps.setInt(1, selectedProduct.getId());
// هنا قمنا بتنفيذ الإستعلام
// هنا قمنا بإغلاق الإتصال مع خادم قاعدة البيانات
// إذا كان يوجد صورة موضوعة للمنتج سيتم مسحها أيضاً لأنه لم يعد هناك حاجة للإحتفاظ بها على جهاز المستخدم
Common.deleteImage(selectedProduct.getImageUrl());
// حتى تكون البيانات المعروضة في الجدول مطابقة للبيانات الموجودة في قاعدة البيانات data هنا قمنا بحجز المنتج من الكائن
data.remove(selectedProduct);
// من أجل تحديث بيانات الجدول, أي لإظهار البيانات التي تم تحديثها فيه refresh() هنا قمنا باستدعاء الدالة
// بعد مسح المنتج بنجاح سيتم عرض المنتج التالي الموجود بعده
// في حال لم يكن هناك أي منتج في الجدول, سيتم تفريغ الحقول الموضوعة بجانب المنتج و إزالة أي صورة كانت معروضة
datePicker.getEditor().setText("");
productImage.setText("No image found");
productImage.setGraphic(null);
// في حال حدوث أي خطأ, سيتم عرضه بداخل نافذة منبثقة
alert.show("Error", e.getMessage(), AlertType.ERROR);
// dateFormat بالشكل ( الفورمات ) الذي قمنا بتحديده في المتغير datePicker هنا قمنا بتعريف دالة خاصة لإرجاع التاريخ الذي يختاره المستخدم بواسطة الكائن
public StringConverter dateFormatter()
StringConverter converter = new StringConverter<LocalDate>() {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(dateFormat);
public String toString(LocalDate date) {
return dateFormatter.format(date);
public LocalDate fromString(String string) {
if (string != null && !string.isEmpty()) {
return LocalDate.parse(string, dateFormatter);
// و التي سنستخدمها كلما أردنا الإتصال بقاعدة البيانات getConnection() هنا قمنا ببناء الدالة
private Connection getConnection()
// DBInfo معلومات الإتصال بقاعدة البيانات قمنا بجلبها من الإنترفيس
con = DriverManager.getConnection(DB_NAME_WITH_ENCODING, USER, PASSWORD);
alert.show("Connection Error", e.getMessage(), AlertType.ERROR);
// لتحديد كيف سيتم جلب البيانات من قاعدة البيانات و عرضها في الجدول عند تشغيل البرنامج fillTheTable() هنا قمنا ببناء الدالة
private void fillTheTable()
// products هنا قمنا بالإتصال بقاعدة البيانات و بتجهيز الإستعلام الذي سيجلب جميع قيم الجدول
Connection con = getConnection();
String query = "SELECT * FROM products";
// لتخزين نتيجة الإستعلام rs لتنفيذ الإستعلام, و الكائن st هنا قمنا بإنشاء الكائن
// rs هنا قمنا بتنفيذ الإستعلام و تخزين نتيجته في الكائن
st = con.createStatement();
rs = st.executeQuery(query);
// في كل مرة rs لتخزين منتج واحد من المنتجات التي ستكون موجودة في الكائن product هنا قمنا بإنشاء الكائن
// الحلقة التالية ترجع سطر واحد في كل مرة, أي معلومات منتج واحد
// product بيانات المنتج التي سيتم إرجاعها في كل مرة سيتم تخزينها بشكل مؤقت في الكائن
Product product = new Product();
product.setId(rs.getInt("id"));
product.setName(rs.getString("name"));
product.setPrice(Double.parseDouble(rs.getString("price")));
product.setAddedDate(rs.getDate("added_date").toString());
product.setImageUrl(rs.getString("image_url"));
// data كعنصر واحد في الكائن product في الأخير سيتم إضافة الكائن
// هنا قمنا بإغلاق الإتصال مع قاعدة البيانات لأننا لم نعد بحاجة إليها
alert.show("Error", e.getMessage(), AlertType.ERROR);
// لفحص القيم التي أدخلها المستخدم في الحقول للتأكد من صحتها قبل إضافة المنتج في قاعدة البيانات checkInputs() قمنا ببناء الدالة
private boolean checkInputs()
if (nameField.getText().equals("") && priceField.getText().equals("")) {
alert.show("Missing required Fields", "Name and Price fields cannot be empty!", AlertType.WARNING);
else if (nameField.getText().equals("")) {
alert.show("Missing required Fields", "Please enter product name", AlertType.WARNING);
else if (priceField.getText().equals("")) {
alert.show("Missing required Fields", "Please enter product price", AlertType.WARNING);
Float.parseFloat(priceField.getText());
catch (NumberFormatException e) {
alert.show("Error", "Price should be a decimal number (eg: 40, 10.5)", AlertType.ERROR);
import java.io.File;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.FileChooser;
import javafx.util.StringConverter;
// Pane يمثل حاوية النافذة التي سنظهرها عند تشغيل التطبيق لهذا جعلناه يرث من الكلاس AllProductsPane الكلاس
// ليحصل على المعلومات الضرورية التي نحتاجها للإتصال بخادم قاعدة البيانات DBInfo و جعلناه ينفذ الإنترفيس
public class AllProductsPane extends Pane implements DBInfo {
// AllProductsPane هنا قمنا بإنشاء جميع الأشياء التي سنضيفها في الحاوية التي يمثلها الكائن الذي ننشئه من الكلاس
TableView table = new TableView();
TableColumn<Product, Integer> columnId = new TableColumn("ID");
TableColumn<Product, String> columnName = new TableColumn("Name");
TableColumn<Product, Double> columnPrice = new TableColumn("Price ($)");
TableColumn<Product, String> columnAddedDate = new TableColumn("Added Dated");
Label productImage = new Label("No image found");
Label idLabel = new Label("ID");
Label nameLabel = new Label("Name");
Label priceLabel = new Label("Price");
Label dateLabel = new Label("Date");
Label moveFastLabel = new Label("Move Fast");
Label searchLabel = new Label("Search");
TextField idField = new TextField();
TextField nameField = new TextField();
TextField priceField = new TextField();
TextField searchField = new TextField();
Button updateImageButton = new Button("Update Image");
Button insertButton = new Button("Add New", new ImageView(new Image(getClass().getResourceAsStream("/images/insert.png"))));
Button updateButton = new Button("Update", new ImageView(new Image(getClass().getResourceAsStream("/images/update.png"))));
Button deleteButton = new Button("Delete", new ImageView(new Image(getClass().getResourceAsStream("/images/delete.png"))));
Button selectFirstButton = new Button("First", new ImageView(new Image(getClass().getResourceAsStream("/images/first.png"))));
Button selectLastButton = new Button("Last", new ImageView(new Image(getClass().getResourceAsStream("/images/last.png"))));
Button selectNextButton = new Button("Next", new ImageView(new Image(getClass().getResourceAsStream("/images/next.png"))));
Button selectPreviousButton = new Button("Previous", new ImageView(new Image(getClass().getResourceAsStream("/images/previous.png"))));
Button exitButton = new Button("Exit", new ImageView(new Image(getClass().getResourceAsStream("/images/exit.png"))));
DatePicker datePicker = new DatePicker();
// التي سنعرضها في الجدول ( Product سنخزن فيه معلومات جميع المنتجات ( التي هي عبارة عن كائنات من الكلاس data الكائن
ObservableList<Product> data = FXCollections.observableArrayList();
// و الذي يمثل النافذة المنبثقة التي تسمح لنا بإضافة منتج جديد AddNewProductDialog هنا قمنا بإنشاء كائن من الكلاس
AddNewProductDialog addProductDialog = new AddNewProductDialog(data);
// الكائن التالي سنخزن فيه الصورة التي تظهر للمنتج الذي يتم النقر عليه في الجدول أو التي يختارها المستخدم بشكل مؤقت عند تحديث صورة المنتج
File selectedFile = null;
// ليظهر كالتالي: يوم - شهر - سنة datePicker سنستخدمه لتحديد الطريقة التي سيظهر بها التاريخ بداخل الكائن dateFormat المتغير
final String dateFormat = "yyyy-MM-dd";
// لأننا سنستخدمه لعرض أي خطأ أو معلومة أمام المستخدم بداخل نافذة منبثقة صغيرة بشكل مختصر SpecialAlert هنا قمنا بإنشاء كائن من الكلاس
SpecialAlert alert = new SpecialAlert();
// AllProductsPane هذا كونستركتور الكلاس
public AllProductsPane() {
// dateFormat يظهر بالفورمات الذي يشير إليه المتغير datePicker لها لجعل التاريخ الذي يظهر في الكائن dateFormatter() و تمرير الدالة setConverter() هنا قمنا باستدعاء الدالة
datePicker.setConverter(dateFormatter());
// table هنا وضعنا الأعمدة الأربعة في الكائن
table.getColumns().addAll(columnId, columnName, columnPrice, columnAddedDate);
// table سيتم عرضها في الكائن data هنا قمنا بتحديد أن البيانات التي ستكون موجودة في الكائن
table.setItems(data);
// تم إضافته في الجدول Product الموجودة في كل كائن id سيُعرض فيه قيم الـ columnId هنا قلنا أن العامود
columnId.setCellValueFactory(new PropertyValueFactory<>("id"));
// تم إضافته في الجدول Product الموجودة في كل كائن name سيُعرض فيه قيم الـ columnName هنا قلنا أن العامود
columnName.setCellValueFactory(new PropertyValueFactory<>("name"));
// تم إضافته في الجدول Product الموجودة في كل كائن price سيُعرض فيه قيم الـ columnPrice هنا قلنا أن العامود
columnPrice.setCellValueFactory(new PropertyValueFactory<>("price"));
// تم إضافته في الجدول Product الموجودة في كل كائن addedDate سيُعرض فيه قيم الـ columnAddedDate هنا قلنا أن العامود
columnAddedDate.setCellValueFactory(new PropertyValueFactory<>("addedDate"));
// هنا قمنا بجعل حجم أعمدة الجدول ينقسم بالتساوي على عددهم مع إبقاء المستخدم قادر على تعديل أحجامهم بواسطة الفأرة
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
// AllProductsPane هنا قمنا بتحديد حجم كل شيء سيتم إضافته في الحاوية التي يمثلها الكائن الذي ننشئه من الكلاس
productImage.setPrefSize(270, 244);
updateImageButton.setPrefSize(130, 35);
idLabel.setPrefSize(50, 40);
idField.setPrefSize(270, 40);
nameLabel.setPrefSize(50, 40);
nameField.setPrefSize(270, 40);
priceLabel.setPrefSize(50, 40);
priceField.setPrefSize(270, 40);
dateLabel.setPrefSize(50, 40);
datePicker.setPrefSize(270, 40);
deleteButton.setPrefSize(130, 40);
updateButton.setPrefSize(130, 40);
table.setPrefSize(520, 505);
searchField.setPrefSize(255, 36);
searchLabel.setPrefSize(115, 40);
insertButton.setPrefSize(130, 60);
moveFastLabel.setPrefSize(190, 30);
selectFirstButton.setPrefSize(130, 40);
selectLastButton.setPrefSize(130, 40);
selectNextButton.setPrefSize(130, 40);
selectPreviousButton.setPrefSize(130, 40);
exitButton.setPrefSize(130, 40);
// AllProductsPane هنا قمنا بتحديد مكان ظهور كل شيء سيتم إضافته في الحاوية التي يمثلها الكائن الذي ننشئه من الكلاس
productImage.setTranslateX(80);
productImage.setTranslateY(40);
updateImageButton.setTranslateX(150);
updateImageButton.setTranslateY(295);
idLabel.setTranslateX(20);
idLabel.setTranslateY(355);
idField.setTranslateX(80);
idField.setTranslateY(355);
nameLabel.setTranslateX(20);
nameLabel.setTranslateY(405);
nameField.setTranslateX(80);
nameField.setTranslateY(405);
priceLabel.setTranslateX(20);
priceLabel.setTranslateY(455);
priceField.setTranslateX(80);
priceField.setTranslateY(455);
dateLabel.setTranslateX(20);
dateLabel.setTranslateY(505);
datePicker.setTranslateX(80);
datePicker.setTranslateY(505);
deleteButton.setTranslateX(80);
deleteButton.setTranslateY(575);
updateButton.setTranslateX(220);
updateButton.setTranslateY(575);
table.setTranslateX(377);
table.setTranslateY(40);
searchField.setTranslateX(530);
searchField.setTranslateY(577);
searchLabel.setTranslateX(460);
searchLabel.setTranslateY(575);
insertButton.setTranslateX(920);
insertButton.setTranslateY(40);
moveFastLabel.setTranslateX(890);
moveFastLabel.setTranslateY(150);
selectFirstButton.setTranslateX(920);
selectFirstButton.setTranslateY(200);
selectLastButton.setTranslateX(920);
selectLastButton.setTranslateY(250);
selectNextButton.setTranslateX(920);
selectNextButton.setTranslateY(300);
selectPreviousButton.setTranslateX(920);
selectPreviousButton.setTranslateY(350);
exitButton.setTranslateX(920);
exitButton.setTranslateY(575);
// AllProductsPane هنا قمنا بتحديد خصائص الأشياء التي سيتم إضافتها في الحاوية التي يمثلها الكائن الذي ننشئه من الكلاس
updateImageButton.setFont(Font.font("Arial", FontWeight.BOLD, 13));
idLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
idField.setFont(Font.font("Arial", FontWeight.BOLD, 15));
nameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
nameField.setFont(Font.font("Arial", FontWeight.BOLD, 15));
priceLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
priceField.setFont(Font.font("Arial", FontWeight.BOLD, 15));
dateLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16));
datePicker.getEditor().setFont(Font.font("Arial", FontWeight.BOLD, 15));
deleteButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
updateButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
insertButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
searchField.setFont(Font.font("Arial", FontWeight.BOLD, 15));
searchLabel.setFont(Font.font("Arial", FontWeight.BOLD, 17));
moveFastLabel.setFont(Font.font("Arial", FontWeight.BOLD, 22));
selectFirstButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
selectLastButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
selectNextButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
selectPreviousButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
exitButton.setFont(Font.font("Arial", FontWeight.BOLD, 16));
productImage.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
idField.setStyle("-fx-border-color: lightgray; -fx-border-width: 2; -fx-background-color: #eee;");
nameField.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
priceField.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
searchField.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
datePicker.setStyle("-fx-border-color: lightgray; -fx-border-width: 2;");
productImage.setAlignment(Pos.CENTER);
moveFastLabel.setAlignment(Pos.CENTER);
idField.setEditable(false);
// AllProductsPane هنا قمنا بإضافة جميع الأشياء التي قمنا بإنشائها و تعديل تصيممها في الكائن الذي ننشئه من الكلاس
this.getChildren().add(table);
this.getChildren().add(productImage);
this.getChildren().add(updateImageButton);
this.getChildren().add(idLabel);
this.getChildren().add(idField);
this.getChildren().add(nameLabel);
this.getChildren().add(nameField);
this.getChildren().add(priceLabel);
this.getChildren().add(priceField);
this.getChildren().add(dateLabel);
this.getChildren().add(datePicker);
this.getChildren().add(insertButton);
this.getChildren().add(updateButton);
this.getChildren().add(deleteButton);
this.getChildren().add(searchField);
this.getChildren().add(searchLabel);
this.getChildren().add(moveFastLabel);
this.getChildren().add(selectFirstButton);
this.getChildren().add(selectLastButton);
this.getChildren().add(selectNextButton);
this.getChildren().add(selectPreviousButton);
this.getChildren().add(exitButton);
// لملئ الجدول بالمنتجات fillTheTable() هنا قمنا باستدعاء الدالة
fillTheTable();
// لإظهار معلومات و صورة أول منتج في الجدول في الحقول الموضوعة بجانبه showFirstProduct() هنا قمنا باستدعاء الدالة
showFirstProduct();
// لعرض معلومات المنتج بجانب الجدول showProduct() سيتم استدعاء الدالة table هنا قمنا بتحديد أنه عند النقر على أي سطر في الجدول الذي يمثله الكائن
table.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if (newSelection != null) {
showProduct(table.getSelectionModel().getSelectedIndex());
}
});
// حتى يتم إظهار النافذة المنبثقة addProductDialog من الكائن display() سيتم استدعاء الدالة insertButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
insertButton.setOnAction(Action -> {
addProductDialog.display(true);
});
// لعرض معلومات أول منتج في الجدول بجانبه showFirstProduct() سيتم استدعاء الدالة selectFirstButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
selectFirstButton.setOnAction(Action -> {
showFirstProduct();
});
// لعرض معلومات آخر منتج في الجدول بجانبه showLastProduct() سيتم استدعاء الدالة selectLastButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
selectLastButton.setOnAction(Action -> {
showLastProduct();
});
// لعرض معلومات المنتج التالي في الجدول بجانبه showNextProduct() سيتم استدعاء الدالة selectNextButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
selectNextButton.setOnAction(Action -> {
showNextProduct();
});
// لعرض معلومات المنتج السابق في الجدول بجانبه showPreviousProduct() سيتم استدعاء الدالة selectPreviousButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
selectPreviousButton.setOnAction(Action -> {
showPreviousProduct();
});
// لفتح نافذة منبثقة تسمح بتغير صورة المنتج chooseImage() سيتم استدعاء الدالة updateImageButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
updateImageButton.setOnAction(Action -> {
updateImage();
});
// لتحديث بيانات المنتج في قاعدة البيانات و في الجدول updateProduct() سيتم استدعاء الدالة updateButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
updateButton.setOnAction(Action -> {
updateProduct();
});
// لحذف المنتج و صورته بشكل نهائي deleteProduct() سيتم استدعاء الدالة deleteButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
deleteButton.setOnAction(Action -> {
deleteProduct();
});
// لإغلاق البرنامج exit() سيتم استدعاء الدالة exitButton هنا قمنا بتحديد أنه عند النقر على الزر الذي يمثله الكائن
exitButton.setOnAction(Action -> {
System.exit(0);
});
// للبحث في الجدول بناءاً على النص الذي يتم إدخاله search() سيتم استدعاء الدالة searchField هنا قمنا بتحديد أنه عند كتابة أو مسح أي حرف في مربع النص الذي يمثله الكائن
searchField.textProperty().addListener((obs, oldText, newText) -> {
search();
});
}
// searchField لتحديد كيف سيتم البحث في الجدول عند إدخال أي كلمة في مربع البحث الذي يمثله الكائن search() هنا قمنا ببناء الدالة
private void search()
{
// keyword سيتم تخزينه بشكل مؤقت في المتغير searchField النص الذي يتم إدخاله في الكائن
String keyword = searchField.getText();
// في الجدول data سيتم عرض جميع البيانات الموجودة في الكائن searchField إذا كان لا يوجد أي نص مدخل في الكائن
if (keyword.equals("")) {
table.setItems(data);
}
// data سيتم إنشاء نسخة من البيانات ( أي المنتجات ) الموجودة في الكائن searchField إذا كان يوجد أي نص مدخل في الكائن
// keyword فيها ) على نفس الأحرف الموجودة في المتغير name التي يحتوي إسمها ( أو الخاصية
else {
ObservableList<Product> filteredData = FXCollections.observableArrayList();
for (Product product : data) {
if(product.getName().contains(keyword))
filteredData.add(product);
}
// في الأخير سيتم عرض نسخة البيانات المفلترة التي تم إنشاؤها في الجدول بدلاً من البيانات الأصلية التي كانت معروضة فيه
table.setItems(filteredData);
}
}
// و التي سنقوم باستدعائها كلما تم تحديد منتج جديد في الجدول لعرض جميع المعلومات showProduct() هنا قمنا ببناء الدالة
// الخاص فيه id المتوفرة عنه بجانب الجدول, مع الإشارة إلى أنه سيتم جلب جميع المعلومات الخاصة بالمنتج بناءاً على رقم الـ
private void showProduct(int index)
{
idField.setText(data.get(index).getId() + "");
nameField.setText(data.get(index).getName());
priceField.setText(data.get(index).getPrice() + "");
datePicker.getEditor().setText(data.get(index).getAddedDate());
// في حال كان المنتج لا يملك صورة "No image found" الشرطين التاليين فكرتهما أنه سيتم وضع النص
if (data.get(index).getImageUrl() == null) {
productImage.setGraphic(null);
productImage.setText("No image found");
}
else {
productImage.setText("");
productImage.setGraphic(new ImageView(new Image(
new File(data.get(index).getImageUrl()).toURI().toString(),
224, 224, true, true)));
}
}
// لتحديد أول منتج في الجدول ثم عرض معلوماته showFirstProduct() هنا قمنا ببناء الدالة
private void showFirstProduct()
{
if (!data.isEmpty()) {
table.getSelectionModel().select(0);
showProduct(0);
}
}
// لتحديد آخر منتج في الجدول ثم عرض معلوماته showlastProduct() هنا قمنا ببناء الدالة
private void showLastProduct()
{
if (!data.isEmpty()) {
table.getSelectionModel().select(data.size() - 1);
showProduct(data.size() - 1);
}
}
// لتحديد المنتج التالي في الجدول ثم عرض معلوماته showNextProduct() هنا قمنا ببناء الدالة
private void showNextProduct()
{
if (table.getSelectionModel().getSelectedIndex() < data.size() - 1) {
int currentSelectedRow = table.getSelectionModel().getSelectedIndex() + 1;
table.getSelectionModel().select(currentSelectedRow);
showProduct(currentSelectedRow);
}
}
// لتحديد المنتج السابق في الجدول ثم عرض معلوماته showPreviousProduct() هنا قمنا ببناء الدالة
private void showPreviousProduct()
{
if (table.getSelectionModel().getSelectedIndex() > 0) {
int currentSelectedRow = table.getSelectionModel().getSelectedIndex() - 1;
table.getSelectionModel().select(currentSelectedRow);
showProduct(currentSelectedRow);
}
}
// لجعل المستخدم قادر على إختيار صورة موجودة في حاسوبه updateImage() قمنا ببناء الدالة
// updateImageButton سيتم إستدعاء هذه الدالة عندما يقوم المستخدم بالنقر على الزر الذي يمثله الكائن
private void updateImage()
{
// الشرط التالي معناه أنه إذا لم يكن هناك أي سطر محدد في الجدول أثناء النقر على الزر سيتم إظهار رسالة
// تنبيه تبلغ المستخدم أنه يجب أن يحدد السطر ( أي المنتج ) الذي يريد تحديث صورته قبل أن ينقر على الزر
if(table.getSelectionModel().getSelectedItem() == null)
{
alert.show("Message", "Select the item that you want to update its image first", AlertType.INFORMATION);
return;
}
// و الذي سيمثل نافذة منبثقة لإختيار صورة من الجهاز FileChooser هنا قمنا بإنشاء كائن من الكلاس
FileChooser fileChooser = new FileChooser();
// هنا قمنا بتحديد نوع الصور التي يمكنك للمستخدم إختيارها من جهازه
fileChooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter("Select a .JPG .PNG .GIF image", "*.jpg", "*.png", "*.gif")
);
// لتخزين الملف الذي قد يختاره المستخدم من الحاسوب selectedFile و قمنا بتجهيز الكائن showOpenDialog() بواسطة الدالة fileChooser هنا قمنا بإظهار الكائن
selectedFile = fileChooser.showOpenDialog(null);
// Open فهذا يعني أن المستخدم قام باختيار صورة موجودة على جهازه و نقر على الزر null لا تساوي selectedFile في حال كانت قيمة الكائن
if (selectedFile != null) {
try {
// createImagePath لإنشاء نسخة من الصورة التي اختارها مع حفظ المسار الذي أنشئت فيه في المتغير saveSelectedImage() هنا قمنا باستدعاء الدالة
String createImagePath = Common.saveSelectedImage(selectedFile);
// لتخزين نص الإستعلام الذي سيتم تنفيذه query هنا قمنا بالإتصال بخادم قاعدة البيانات و تجهزي المتغير
Connection con = Common.getConnection();
// الخاص بالمنتج id هنا قمنا بتجهيز نص الإستعلام الذي يقضي بتحديث مسار الصورة بناءاً على رقم الـ
String query = "UPDATE products SET image_url = ? WHERE id = ?";
// و الذي سيسمح لنا بتنفيذ الإستعلام PreparedStatement هنا قمنا بتجهيز كائن نوعه
PreparedStatement ps = con.prepareStatement(query);
// الخاص بالمنتج مكان ثاني علامة إستفهام id هنا مررنا مسار الصورة مكان أول علامة إستفهام و رقم الـ
ps.setString(1, createImagePath);
ps.setInt(2, Integer.parseInt(idField.getText()));
// هنا قمنا بتنفيذ الإستعلام
ps.executeUpdate();
// هنا قمنا بإغلاق الإتصال مع خادم قاعدة البيانات
con.close();
// selectedProduct هنا قمنا بتخزين بيانات المنتج الحالي الذي تم تغيير معلوماته في الكائن
Product selectedProduct = (Product) table.getSelectionModel().getSelectedItem();
// هنا قمنا بمسح صورة المنتج القديمة من على جهاز المستخدم
Common.deleteImage(selectedProduct.getImageUrl());
// data و الذي سيتحدث بشكل تلقائي في الكائن selectedProduct هنا قمنا بتحديث رابط صورة المنتج الجديدة في الكائن
selectedProduct.setImageUrl(createImagePath);
// هنا قمنا بعرض صورة المنتج الجديدة
productImage.setText("");
productImage.setGraphic(new ImageView(new Image(
selectedFile.toURI().toString(), 224, 224, true, true)));
}
catch (Exception ex) {
alert.show("Error", "Failed to update Image", AlertType.ERROR);
}
}
}
// لتحديد كيف سيتم تحديث بيانات المنتج في قاعدة البيانات updateProduct() قمنا ببناء الدالة
// updateButton سيتم إستدعاء هذه الدالة عندما يقوم المستخدم بالنقر على الزر الذي يمثله الكائن
private void updateProduct()
{
// الشرط التالي معناه أنه إذا لم يكن هناك أي سطر محدد في الجدول أثناء النقر على الزر سيتم إظهار رسالة
// تنبيه تبلغ المستخدم أنه يجب أن يحدد السطر ( أي المنتج ) الذي يريد تحديث بياناته قبل أن ينقر على الزر
if(table.getSelectionModel().getSelectedItem() == null)
{
alert.show("Message", "Select the item that you want to update first", AlertType.INFORMATION);
return;
}
// الشرط التالي معناه أنه إذا تم التشييك على الحقول و كان لا يوجد أي خطأ أو نقص في المعلومات المطلوب إدخالها
// سيتم تنفيذ باقي الأوامر الموجودة في الدالة تحديث معلومات المنتج في قاعدة البيانات و في الجدول أيضاً
if (checkInputs() == false) {
return;
}
// selectedProduct هنا قمنا بتخزين بيانات المنتج الحالي الذي تم تغيير معلوماته في الكائن
Product selectedProduct = (Product) table.getSelectionModel().getSelectedItem();
try {
// لتخزين نص الإستعلام الذي سيتم تنفيذه query هنا قمنا بالإتصال بخادم قاعدة البيانات و تجهزي المتغير
Connection con = getConnection();
String query;
// إذا كان المنتج لا يملك صورة, سيتم تحديث كل حقوله في قاعدة البيانات مع عدا حقل الصورة
query = "UPDATE products SET name = ?, price = ?, added_date = ? WHERE id = ?";
PreparedStatement ps = con.prepareStatement(query);
ps.setString(1, nameField.getText());
ps.setDouble(2, Double.valueOf(priceField.getText()));
ps.setDate(3, Date.valueOf(datePicker.getEditor().getText()));
ps.setInt(4, Integer.parseInt(idField.getText()));
ps.executeUpdate();
// هنا قمنا بإغلاق الإتصال مع خادم قاعدة البيانات
con.close();
// هنا قمنا بوضوع جميع المعلومات الجديدة في الكائن الذي تم تحديده في الأساس في الجدول
selectedProduct.setName(nameField.getText());
selectedProduct.setPrice(Double.valueOf(priceField.getText()));
selectedProduct.setAddedDate(datePicker.getEditor().getText());
// من أجل تحديث بيانات الجدول, أي لإظهار البيانات التي تم تحديثها فيه refresh() هنا قمنا باستدعاء الدالة
table.refresh();
// هنا قمنا بإظهار نافذة منبثقة تخبرنا أنه تم تحديث البيانات بنجاح
alert.show("Product Successfully updated",
"Product information has been successfully updated",
AlertType.INFORMATION);
}
catch (Exception e) {
// في حال حدوث أي خطأ, سيتم عرضه بداخل نافذة منبثقة
alert.show("Error", e.getMessage(), AlertType.ERROR);
}
}
// لتحديد كيف سيتم تحديث بيانات المنتج في قاعدة البيانات updateProduct() قمنا ببناء الدالة
// updateButton سيتم إستدعاء هذه الدالة عندما يقوم المستخدم بالنقر على الزر الذي يمثله الكائن
private void deleteProduct()
{
// الشرط التالي معناه أنه إذا لم يكن هناك أي سطر محدد في الجدول أثناء النقر على الزر سيتم إظهار رسالة
// تنبيه تبلغ المستخدم أنه يجب أن يحدد السطر ( أي المنتج ) الذي يريد حذفه قبل أن ينقر على الزر
if(table.getSelectionModel().getSelectedItem() == null)
{
alert.show("Message", "Select the item that you want to delete first", AlertType.INFORMATION);
return;
}
// selectedProduct هنا قمنا بتخزين بيانات المنتج الحالي الذي تم تغيير معلوماته في الكائن
Product selectedProduct = (Product) table.getSelectionModel().getSelectedItem();
try {
// لتخزين نص الإستعلام الذي سيتم تنفيذه query هنا قمنا بالإتصال بخادم قاعدة البيانات و تجهزي المتغير
Connection con = getConnection();
// الخاص فيه id هنا قمنا بتجهيز نص الإستعلام الذي يقضي بمسح المنتج بناءاً على رقم الـ
String query = "DELETE FROM products WHERE id = ?";
// و الذي سيسمح لنا بتنفيذ الإستعلام PreparedStatement هنا قمنا بتجهيز كائن نوعه
PreparedStatement ps = con.prepareStatement(query);
// الخاص بالمنتج مكان علامة الإستفهام id هنا مررنا رقم الـ
ps.setInt(1, selectedProduct.getId());
// هنا قمنا بتنفيذ الإستعلام
ps.executeUpdate();
// هنا قمنا بإغلاق الإتصال مع خادم قاعدة البيانات
con.close();
// إذا كان يوجد صورة موضوعة للمنتج سيتم مسحها أيضاً لأنه لم يعد هناك حاجة للإحتفاظ بها على جهاز المستخدم
Common.deleteImage(selectedProduct.getImageUrl());
// حتى تكون البيانات المعروضة في الجدول مطابقة للبيانات الموجودة في قاعدة البيانات data هنا قمنا بحجز المنتج من الكائن
data.remove(selectedProduct);
// من أجل تحديث بيانات الجدول, أي لإظهار البيانات التي تم تحديثها فيه refresh() هنا قمنا باستدعاء الدالة
table.refresh();
// بعد مسح المنتج بنجاح سيتم عرض المنتج التالي الموجود بعده
if(data.size() > 0) {
showNextProduct();
}
// في حال لم يكن هناك أي منتج في الجدول, سيتم تفريغ الحقول الموضوعة بجانب المنتج و إزالة أي صورة كانت معروضة
else
{
idField.setText("");
nameField.setText("");
priceField.setText("");
datePicker.getEditor().setText("");
productImage.setText("No image found");
productImage.setGraphic(null);
}
}
catch (Exception e) {
// في حال حدوث أي خطأ, سيتم عرضه بداخل نافذة منبثقة
alert.show("Error", e.getMessage(), AlertType.ERROR);
}
}
// dateFormat بالشكل ( الفورمات ) الذي قمنا بتحديده في المتغير datePicker هنا قمنا بتعريف دالة خاصة لإرجاع التاريخ الذي يختاره المستخدم بواسطة الكائن
public StringConverter dateFormatter()
{
StringConverter converter = new StringConverter<LocalDate>() {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(dateFormat);
@Override
public String toString(LocalDate date) {
if (date != null) {
return dateFormatter.format(date);
}
return "";
}
@Override
public LocalDate fromString(String string) {
if (string != null && !string.isEmpty()) {
return LocalDate.parse(string, dateFormatter);
}
return null;
}
};
return converter;
}
// و التي سنستخدمها كلما أردنا الإتصال بقاعدة البيانات getConnection() هنا قمنا ببناء الدالة
private Connection getConnection()
{
Connection con;
try {
// DBInfo معلومات الإتصال بقاعدة البيانات قمنا بجلبها من الإنترفيس
con = DriverManager.getConnection(DB_NAME_WITH_ENCODING, USER, PASSWORD);
return con;
}
catch (SQLException e) {
alert.show("Connection Error", e.getMessage(), AlertType.ERROR);
return null;
}
}
// لتحديد كيف سيتم جلب البيانات من قاعدة البيانات و عرضها في الجدول عند تشغيل البرنامج fillTheTable() هنا قمنا ببناء الدالة
private void fillTheTable()
{
// products هنا قمنا بالإتصال بقاعدة البيانات و بتجهيز الإستعلام الذي سيجلب جميع قيم الجدول
Connection con = getConnection();
String query = "SELECT * FROM products";
// لتخزين نتيجة الإستعلام rs لتنفيذ الإستعلام, و الكائن st هنا قمنا بإنشاء الكائن
Statement st;
ResultSet rs;
try {
// rs هنا قمنا بتنفيذ الإستعلام و تخزين نتيجته في الكائن
st = con.createStatement();
rs = st.executeQuery(query);
// في كل مرة rs لتخزين منتج واحد من المنتجات التي ستكون موجودة في الكائن product هنا قمنا بإنشاء الكائن
// الحلقة التالية ترجع سطر واحد في كل مرة, أي معلومات منتج واحد
while (rs.next()) {
// product بيانات المنتج التي سيتم إرجاعها في كل مرة سيتم تخزينها بشكل مؤقت في الكائن
Product product = new Product();
product.setId(rs.getInt("id"));
product.setName(rs.getString("name"));
product.setPrice(Double.parseDouble(rs.getString("price")));
product.setAddedDate(rs.getDate("added_date").toString());
product.setImageUrl(rs.getString("image_url"));
// data كعنصر واحد في الكائن product في الأخير سيتم إضافة الكائن
data.add(product);
}
// هنا قمنا بإغلاق الإتصال مع قاعدة البيانات لأننا لم نعد بحاجة إليها
con.close();
}
catch (SQLException e) {
alert.show("Error", e.getMessage(), AlertType.ERROR);
}
}
// لفحص القيم التي أدخلها المستخدم في الحقول للتأكد من صحتها قبل إضافة المنتج في قاعدة البيانات checkInputs() قمنا ببناء الدالة
private boolean checkInputs()
{
if (nameField.getText().equals("") && priceField.getText().equals("")) {
alert.show("Missing required Fields", "Name and Price fields cannot be empty!", AlertType.WARNING);
return false;
}
else if (nameField.getText().equals("")) {
alert.show("Missing required Fields", "Please enter product name", AlertType.WARNING);
return false;
}
else if (priceField.getText().equals("")) {
alert.show("Missing required Fields", "Please enter product price", AlertType.WARNING);
return false;
}
try {
Float.parseFloat(priceField.getText());
return true;
}
catch (NumberFormatException e) {
alert.show("Error", "Price should be a decimal number (eg: 40, 10.5)", AlertType.ERROR);
return false;
}
}
}