Skip to content

Commit

Permalink
temp
Browse files Browse the repository at this point in the history
  • Loading branch information
peretr committed May 15, 2026
1 parent 7e6e604 commit 9fa7194
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package edu.ntnu.idi.idatt2003.gruppe42.Controller;

import edu.ntnu.idi.idatt2003.gruppe42.Model.Exceptions.StockFileParseException;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Exchange;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Stock;
import edu.ntnu.idi.idatt2003.gruppe42.Model.StockFileHandler;
import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.StockApp;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
Expand All @@ -29,9 +32,12 @@ public MarketController() {

startMarket();
System.out.println("Market loaded");

} catch (Exception exception) {
} catch (IOException exception) {
System.out.println("File not found");
} catch (URISyntaxException exception) {
System.out.println("Invalid file path");
} catch (StockFileParseException exception) {
System.out.println("Invalid stock file format");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package edu.ntnu.idi.idatt2003.gruppe42.Controller.ViewControllers;

import edu.ntnu.idi.idatt2003.gruppe42.Audio.Audio;
import edu.ntnu.idi.idatt2003.gruppe42.Audio.AudioManager;
import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.AppStoreController;
import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.BankAppController;
import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.StockAppController;
Expand All @@ -10,7 +8,7 @@
import edu.ntnu.idi.idatt2003.gruppe42.Controller.PopupsController;
import edu.ntnu.idi.idatt2003.gruppe42.Controller.TimeAndWeatherController;
import edu.ntnu.idi.idatt2003.gruppe42.Model.App;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Day;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Exceptions.StockFileParseException;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Player;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction.Purchase;
import edu.ntnu.idi.idatt2003.gruppe42.Model.Transaction.Sale;
Expand Down Expand Up @@ -70,6 +68,7 @@ public DesktopViewController(final Player player, final GameController gameContr

this.marketController = new MarketController();


AppStoreApp appStoreApp = new AppStoreApp();
gameController.addAppController(new AppStoreController(appStoreApp));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package edu.ntnu.idi.idatt2003.gruppe42.Model.Exceptions;

public class StockFileParseException extends Exception {
private final int lineNumber;
private final String lineContent;

public StockFileParseException(int lineNumber, String lineContent, String reason) {
super(String.format("Parse error on line %d: %s%n Content : \"%s\"", lineNumber, reason, lineContent));
this.lineNumber = lineNumber;
this.lineContent = lineContent;
}

public int getLineNumber() { return lineNumber; }
public String getLineContent() { return lineContent; }
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
package edu.ntnu.idi.idatt2003.gruppe42.Model;

import edu.ntnu.idi.idatt2003.gruppe42.Model.Exceptions.StockFileParseException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
* Utility class for handling file data.
* Includes methods for reading and writing from file.
*/
public final class StockFileHandler {
private static final Pattern SYMBOL_PATTERN = Pattern.compile("^[A-Z]{3}$");
private static final Pattern PRICE_PATTERN = Pattern.compile("^\\d+\\.\\d{2}$");
private static final int EXPECTED_FIELDS = 3;

/**
* Private constructor to prevent instantiation.
*/
Expand All @@ -28,62 +35,85 @@ private StockFileHandler() {
* @return list of stock objects
* @throws IOException if file not found or error occurs
*/
public static List<Stock> readFromFile(final Path path) throws IOException {

public static List<Stock> readFromFile(final Path path)
throws IOException, StockFileParseException {

if (!Files.exists(path)) {
throw new IOException("File not found: " + path);
}
if (!Files.isReadable(path)) {
throw new IOException("File is not readable: " + path);
}

List<Stock> stocks = new ArrayList<>();
int lineNumber = 0;

try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(path.toFile()), StandardCharsets.UTF_8))) {

try (BufferedReader bufferedReader = new BufferedReader(
new FileReader(path.toFile()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
line = line.trim();
while ((line = reader.readLine()) != null) {
lineNumber++;
String trimmed = line.trim();

if (line.isEmpty() || line.startsWith("#")) {
// Skip blank lines and comments
if (trimmed.isEmpty() || trimmed.startsWith("#")) {
continue;
}

String[] parts = line.split(",");
String[] parts = trimmed.split(",");
if (parts.length != EXPECTED_FIELDS) {
throw new StockFileParseException(lineNumber, trimmed,
String.format("Expected %d comma-separated fields but found %d. "
+ "Hint: decimal prices must use '.' not ','",
EXPECTED_FIELDS, parts.length));
}

String symbol = parts[0].trim();
if (symbol.isEmpty()) {
throw new StockFileParseException(lineNumber, trimmed,
"Ticker symbol is empty");
}
if (!SYMBOL_PATTERN.matcher(symbol).matches()) {
throw new StockFileParseException(lineNumber, trimmed,
String.format("Ticker symbol \"%s\" is invalid — must be exactly 3 uppercase letters (A-Z)", symbol));
}

String company = parts[1].trim();
if (company.isEmpty()) {
throw new StockFileParseException(lineNumber, trimmed,
"Company name is empty");
}

String rawPrice = parts[2].trim();
if (!PRICE_PATTERN.matcher(rawPrice).matches()) {
throw new StockFileParseException(lineNumber, trimmed,
String.format("Price \"%s\" is invalid — must be a positive number with exactly 2 decimal places using '.' (e.g. 191.27)", rawPrice));
}

final int symbolIndex = 0;
final int companyIndex = 1;
final int priceIndex = 2;
BigDecimal price;
try {
price = new BigDecimal(rawPrice);
} catch (NumberFormatException e) {
throw new StockFileParseException(lineNumber, trimmed,
String.format("Price \"%s\" could not be parsed as a number", rawPrice));
}

String symbol = parts[symbolIndex];
String company = parts[companyIndex];
BigDecimal salePrice = new BigDecimal(parts[priceIndex].trim());
if (price.compareTo(BigDecimal.ZERO) <= 0) {
throw new StockFileParseException(lineNumber, trimmed,
String.format("Price \"%s\" must be greater than zero", rawPrice));
}

stocks.add(new Stock(symbol, company, salePrice));
stocks.add(new Stock(symbol, company, price));
}
}
return stocks;
}

/**
* Method for writing stock information to file.
*
* @param path the path of the file to write to
* @param stocks list of stock objects to be transcribed
* @throws IOException if error occurs
*/
public static void writeToFile(
final Path path,
final List<Stock> stocks
) throws IOException {

try (BufferedWriter writer = Files.newBufferedWriter(path)) {
writer.write("# S&P 500 Companies by Market Cap");
writer.write("\n# Ticker,Name,Price");
writer.newLine();

for (Stock stock : stocks) {
String line = String.format("%s,%s,%s",
stock.getSymbol(),
stock.getCompany(),
stock.getSalesPrice());

writer.write(line);
writer.newLine();
}
if (stocks.isEmpty()) {
throw new StockFileParseException(lineNumber, "",
"File contained no valid stock entries");
}

return stocks;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@ public class StockFileHandlerTest {
void readFromFileTest() throws IOException {
Path testFile = Files.createTempFile("stocks", "csv");
Files.writeString(testFile, "AAPL,Apple Inc.,276.43\nMSFT,Microsoft,404.68");
List<Stock> stocks = StockFileHandler.readFromFile(testFile);
assertEquals(2, stocks.size());
assertEquals("AAPL", stocks.get(0).getSymbol());
try {
List<Stock> stocks = StockFileHandler.readFromFile(testFile);
assertEquals(2, stocks.size());
assertEquals("AAPL", stocks.get(0).getSymbol());
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Test
void readFromFileWithCommentsAndEmptyLines() throws IOException {
Path testFile = Files.createTempFile("stocks", "csv");
Files.writeString(testFile, "#Comment\n\nAAPL,Apple Inc.,276.43");
List<Stock> stocks = StockFileHandler.readFromFile(testFile);
assertEquals(1, stocks.size());
}

@Test
void writeToFileTest() throws IOException {
Path testFile = Files.createTempFile("stocks", "csv");
List<Stock> stocks = List.of(new Stock("AAPL", "Apple Inc.", new BigDecimal("276.43")));
StockFileHandler.writeToFile(testFile, stocks);
List<String> lines = Files.readAllLines(testFile);
assertTrue(lines.get(2).startsWith("AAPL,Apple Inc.,276.43"));
try {
List<Stock> stocks = StockFileHandler.readFromFile(testFile);
assertEquals(1, stocks.size());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

0 comments on commit 9fa7194

Please sign in to comment.